第九章Java多线程机制
每个进程都有独立的代码和数据空间,进程间的切换会有很大开销。
线程可以看成轻量级的进程同一类线程共享代码和数据空间,每个线程有独立的运行栈个程序计数器,线程切换开销小。
多线程:在操作系统中能同时运行多个任务(程序)。
多线程:在同一个应用程序中有多个顺序流同时执行。
二、
说明:
Runnable接口https://www.doczj.com/doc/7014957072.html,ng.Runnable
–Java多线程机制的一个重要部分,实际上它只有一个run()方法
–Thread类实现了Runnable接口,相对于Thread类,它更适合于多个线程处理同一资源
–实现Runnable接口的类的对象可以用来创建线程,这时start方法启动此线程就会在此线程上运行run()方法
–在编写复杂程序时相关的类可能已经继承了某个基类,而Java不支持多继承,在这种情况下,便需要通过实现Runnable接口来生成多线程
Thread类https://www.doczj.com/doc/7014957072.html,ng.Thread
–在Java程序中创建多线程的方法之一是继承Thread类
–封装了Java程序中一个线程对象需要拥有的属性和方法
–从Thread类派生一个子类,并创建这个子类的对象,就可以产生一个新的线程。这个子类应该重写Thread 类的run方法,在run方法中写入需要在新线程中执行的语句段。这个子类的对象需要调用start方法来启动,新线程将自动进入run方法。原线程将同时继续往下执行
–Thread类直接继承了Object类,并实现了Runnable接口。它位于https://www.doczj.com/doc/7014957072.html,ng包中,因而程序开头不用import 任何包就可直接使用
例子:Thread\TestThread1.java 开始使用Eclipse
2 一般程序先执行子线程,再执行主线程(方法调用)
package Thread;
public class TestThread1_1 {
public static void main(String args[]) {
Runner1 r = new Runner1();
r.run();
for(int i=0; i<100; i++) {
System.out.println("Main Thread:------" + i);
}
}
}
class Runner1 implements Runnable {
public void run() {
for(int i=0; i<100; i++) {
System.out.println("Runner1 :" + i);
}
}
}
3继承Thread类(继承,不常用)
package Thread;
public class TestThread1_2{
public static void main(String arg[]){
Runner1_2 r = new Runner1_2();
r.start();
for (int i = 0 ;i <= 100; i++){
System.out.println("Main Thread:!!!!"+i);
}
}
}
class Runner1_2 extends Thread{
public void run(){
for (int i = 0 ;i <= 100; i++){
System.out.println("Runner2:!!!!"+i);
}
}
}
2010-4-2 21:04:16 – 2010-4-14 16:28:59
三、线程状态转换
四、线程控制基本方法
Sleep/Join/Yield方法Sleep/Join(需要捕获异常)
例子1 Thread/TestInterrupt.Java (线程结束的方式)
package Thread;
import java.util.*;
public class TestInterrupt {
public static void main(String arg[]){
MyThread thread = new MyThread();
thread.start();
try{Thread.sleep(10000);}//主线程睡眠…
catch(InterruptedException e){}
thread.interrupt();//中断线程。
}
}
class MyThread extends Thread{
boolean flag = true;
public void run(){//重写的方法不能抛出不必被重写方法不同的方法,此处不能写throws InterruptedException
while(flag){
System.out.println("----"+ new Date()+"----");
try{
sleep(1000);
}
catch(InterruptedException e){//捕获抛出的异常
return;//停止
}
}
}
}
例子2 Thread/TestJoin.Java (线程合并方式)
package Thread;
public class TestJoin {
public static void main(String[] args) {
MyThread2 t1 = new MyThread2("abcde");
t1.start();//启动分支线程
try {
t1.join();//把T1分支线程合并到当前线程
} catch (InterruptedException e) {}
for(int i=1;i<=10;i++){
System.out.println("i am main thread");
}
}
}
class MyThread2 extends Thread {
MyThread2(String s){
super(s);
}
public void run(){
for(int i =1;i<=10;i++){
System.out.println("i am "+getName());
try {
sleep(1000);
} catch (InterruptedException e) {
return;
}
}
}
}
例子3 Thread/TestYield.Java (让出Cup)
package Thread;
public class TestYield {
public static void main(String[] args) {
MyThread3 t1 = new MyThread3("t1");
MyThread3 t2 = new MyThread3("t2");
t1.start(); t2.start();
}
}
class MyThread3 extends Thread {
MyThread3(String s){super(s);}
public void run(){
for(int i =1;i<=100;i++){
System.out.println(getName()+": "+i);
if(i%10==0){
yield();
}
}
}
}
五、线程优先级Priority
例子Thread/TestPriority.Java
package Thread;
public class TestPriority {
public static void main(String[] args) {
Thread t1 = new Thread(new T1());
Thread t2 = new Thread(new T2());
t1.setPriority(Thread.NORM_PRIORITY + 3);//提高优先级
t1.start();
t2.start();
}
}
class T1 implements Runnable {
public void run() {
for(int i=0; i<1000; i++) {
System.out.println("T1: " + i);
}
}
}
class T2 implements Runnable {
public void run() {
for(int i=0; i<1000; i++) {
System.out.println("------T2: " + i);
}
}
}
2010年4月14日21:40:41 – 2010-4-15 16:23:44
六、例子TestThread2-6
TestThread2一个线程类可以启动两个线程
package Thread;
public class TestThread2 {//一个线程类可以启动两个线程public static void main(String args[]) {
Runner2 r = new Runner2();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
}
}
class Runner2 implements Runnable {
public void run() {
for(int i=0; i<30; i++) {
System.out.println("No. " + i);
}
}
}
package Thread;
public class TestThread6 {
public static void main(String args[]){
Thread t = new Runner6();
t.start();
for(int i=0; i<50; i++) {
System.out.println("MainThread: " + i);
}
}
}
class Runner6 extends Thread {
public void run() {
System.out.println(Thread.currentThread().isAlive());
for(int i=0;i<50;i++) {
System.out.println("SubThread: " + i);
}
}
}
七、线程同步
引入:两人同时取同一账户的钱两个线程访问同一资源,进程之间协调的问题
解决:在进程访问独占资源时先锁定再访问synchronized 最好只锁定一个对象
package Thread;
public class TestSync implements Runnable {
Timer timer = new Timer();
public static void main(String[] args) {
TestSync test = new TestSync();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
public void run(){
timer.add(Thread.currentThread().getName());//拿到当前线程的名字 }
}
class Timer{
private static int num = 0;
public void add(String name){
//public synchronized void add(String name){ //synchronized "锁定"关键字
//synchronized (this) {//锁定
num ++;
try {Thread.sleep (1);} //让线程睡眠,给另一个线程执行的机会,更容易看到xia catch (InterruptedException e) {}
System.out .println(name+", 你是第"+num +"个使用timer 的线程");
//}
}
}
t2, 你是第2个使用timer 的线程
t1, 你是第2个使用timer 的线程
问题出现在线程执行方法时被另一个线程打断了.
死锁
07_线程同步_3.avi
Thread/TestDeadLock.java
两个线程锁定了互斥的对象
package Thread;
//一个线程类模拟两个线程。用flag 区分
public class TestDeadLock implements Runnable {
public int flag = 1;
static Object o1 = new Object(), o2 = new Object();
public void run() {
System.out .println("flag=" + flag );
if (flag == 1) {
synchronized (o1) {
//锁定o1
try { Thread.sleep (500);
} catch (Exception e) {
e.printStackTrace();
}
synchronized(o2) {//锁定o2
System.out.println("1");
}
}
}
if(flag == 0) {
synchronized(o2) {//锁定o2
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
synchronized(o1) {//锁定o1
System.out.println("0");
}
}
}
}
public static void main(String[] args) {
TestDeadLock td1 = new TestDeadLock();
TestDeadLock td2 = new TestDeadLock();
td1.flag = 1;
td2.flag = 0;
Thread t1 = new Thread(td1);
Thread t2 = new Thread(td2);
t1.start();
t2.start();
}
}
一道面试题:08_线程同步_4.Avi.当m1()方法执行的过程中,另外的线程能执行m2()
八、生产者消费者问题11_线程同步_7_生产者消费者问题.avi XXX.wait() 让当前访问这个线程等待,此时这个线程的锁不存在了XXXX.notify() 唤醒在此对象监视器上等待的单个线程。
一个wait()对应一个notify()
package Thread;
public class ProducerConsumer {
public static void main(String[] args) { SyncStack ss = new SyncStack();
Producer p = new Producer(ss);
Consumer c = new Consumer(ss);
new Thread(p).start();
new Thread(p).start();
new Thread(p).start();
new Thread(c).start();
}
}
class WoTou {
int id;
WoTou(int id) {
this.id = id;
}
public String toString() {
return"WoTou : " + id;
}
}
class SyncStack {
int index = 0;
WoTou[] arrWT = new WoTou[6];
public synchronized void push(WoTou wt) {
while(index == arrWT.length) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notifyAll();// 唤醒
arrWT[index] = wt;
index ++;
}
public synchronized WoTou pop() {
while(index == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notifyAll();
index--;
return arrWT[index];
}
}
class Producer implements Runnable {
SyncStack ss = null;
Producer(SyncStack ss) {
this.ss = ss;
}
public void run() {
for(int i=0; i<20; i++) {
WoTou wt = new WoTou(i);
ss.push(wt);
System.out.println("生产了:" + wt);
try {
Thread.sleep((int)(Math.random() * 200));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable {
SyncStack ss = null;
Consumer(SyncStack ss) {
this.ss = ss;
}
public void run() {
for(int i=0; i<20; i++) {
WoTou wt = ss.pop();
System.out.println("消费了: " + wt);
try {
Thread.sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Wait和Sleep区别
Wait时别的线程可以访问锁定的对象(调用wait方法的时候必须锁定该对象)
Sleep时别的线程不可以访问锁定对象。
sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。
wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。