------- android培训、java培训、期待与您交流! ----------
1.概念:
进程:进行中的程序
线程:进程中不同的进行流程(执行路径)
多线程的好处:解决了多部分代码同时运行的问题。
弊端:CPU的切换会导致效率变低。
2.创建线程的两种方式
1)implements Runnable :实现接口,避免了单继承造成的局限性
2)Extends Thread :继承父类
示例:
买票问题。如果采用Extends Thread类来实现,要把票数Num定义成静态字段,造成效率的降低,这时候采用implements Runnable的方式就可以将票数定义成非静态字段。
package com.cn.test;
/**
* 卖票系统例子区分两种线程创建方式
* 实现方式比继承方式避免了单继承的局限性
*/
class MyThread extends Thread{//继承方式创建
private static int num=1000;//卖票数得静态化
@Override
public void run() {
while(true){
if(num>0){
Thread.yield();
System.out.println(Thread.currentThread().getName()+"@@@@@@@"+num--);
}
}
}
}
class MyRunnable implements Runnable{
private int num=1000;//实现接口方式,因为共用了一个对象产生线程,所以Num字段不用静态化
public void run() {
while(true){
if(num>0){
Thread.yield();
System.out.println(Thread.currentThread().getName()+"@@@@@@@"+num--);
}
}
}
}
public class ConstructTest {
public static void createByEtds(){//通过继承Threads类创建对象
MyThread mt1=new MyThread();
MyThread mt2=new MyThread();
mt1.start();
mt2.start();
}
public static void createByIpl(){//实现方式创建
MyRunnable mr1=new MyRunnable();
new Thread(mr1).start();
new Thread(mr1).start();
new Thread(mr1).start();
new Thread(mr1).start();
}
public static void main(String[] args) {
// createByEtds();//继承方式
createByIpl();//实现方式
}
}
3.线程的安全问题
上述例子会产生零票数与负票数的情况
这是因为在某个线程在执行到Num字段的判断时,cpu的执行权被其他线程抢走,这时候Num字段由于不受保护,会被多线程修改。从而出现了0,-1,-2的情况。
线程安全问题产生的原因:多个线程共享未被保护的数据。解决的办法:给共享资源上一把锁,哪个线程要用,哪个线程拿锁进去用。类比:火车上的卫生间。
同步代码块与同步函数能解决这个问题。
关键字:synchronizied
package com.cn.test;
/**
* 为了演示synchronized函数与代码块的区别,添加了flag
* 同步要两个前提:有两个或以上线程,用的是同一个锁
*/
class TicketThread implements Runnable {
private int num = 1000;
public boolean flag = true;
public void run() {
if (flag) {
while (true) {
synchronized (this) {//同步代码块相当于一把线程锁,保证资源安全
if (num > 0) {
try {
Thread.sleep(10);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(Thread.currentThread().getName()
+ "@@@@@code" + num--);
}
}
}
} else {
while (true) {
show();
}
}
}
private synchronized void show() {//synchronized修饰的函数,锁的对象是this指针
if (num > 0) {
try {
Thread.sleep(10);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(Thread.currentThread().getName() + "@@@@@show"
+ num--);
}
}
}
public class SynchronizedDemo {
public static void main(String[] args) {
TicketThread th=new TicketThread();
new Thread(th).start();
try{Thread.sleep(10);}catch (Exception e) {e.printStackTrace();}//第一个线程启动后sleep(10),让后面的线程启动
th.flag=false;
new Thread(th).start();
}
}
静态同步代码块用的是改函数所属的字节码文件,即对象.getClass()或类.class()
4.死锁
不同线程相互持有其锁,导致程序不能继续进行
package com.cn.test;
/**
* 死锁
*/
class MyDeadLock implements Runnable{
private boolean flag=true;
MyDeadLock(boolean flag){
this.flag=flag;
}
public void run(){
if(flag){
while(true){
synchronized(Locked.lock1){//同时持有两把锁
System.out.println(Thread.currentThread().getName()+"lock1");
synchronized(Locked.lock2){//第二把锁
System.out.println(Thread.currentThread().getName()+"lock2");
}
}
}
}
else {
while(true){
synchronized(Locked.lock2){//第二个线程持有第一个线程的锁
System.out.println(Thread.currentThread().getName()+"lock2");
synchronized(Locked.lock1){
System.out.println(Thread.currentThread().getName()+"lock1");
}
}
}
}
}
}
class Locked{
public static final Object lock1=new Object();
public static final Object lock2=new Object();
}
public class DeadLock {
public static void main(String[] args) {
MyDeadLock ml1=new MyDeadLock(true);
MyDeadLock ml2=new MyDeadLock(false);
new Thread(ml1).start();
try{Thread.sleep(10);}catch(Exception e){e.printStackTrace();}
new Thread(ml2).start();
}
}
5.线程的生命周期
主要涉及方法:sleep(),wait(),notify(),notifyAll()
例子:生产者与消费者
交替的处理机制:首先立flag=false;没有就让消费者wait,转到生产者,生产者++后
再改flag.再唤醒,
flag=false
|--if(flag) flag=true;生产者
|--if(!flag) flag=false;消费者
由于多个生产者/消费者时用notify出问题,改用notifyAll
package com.cn.test;
class Person {
private String name;
private String gender;
private boolean flag = false;//作为交替,设立flag
private int num=1;
public synchronized void set(String name, String gender) {
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name;
this.gender = gender;
num++;
System.out.println(Thread.currentThread().getName()+"生产者"+num);
flag = true;
this.notifyAll();
}
public synchronized void get() {
if (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num--;
System.out.println(Thread.currentThread().getName()+"消费者@@"+this.num);
flag = false;
this.notifyAll();
}
}
class Input implements Runnable {
Person p;
Input(Person p) {
this.p = p;
}
public void run() {
int x = 0;// -------------------循环存
while (true) {
if (x == 0) {
p.set("zhangsan", "nan");
} else
p.set("XIAOHONG", "NV");
x = (x + 1) % 2;
}
}
}
class Output implements Runnable {
Person p;
Output(Person p) {
this.p = p;
}
public void run() {
while (true) {
p.get();
}
}
}
public class MutilpDemo {
public static void main(String[] args) {
Person p = new Person();//初始化资源
Input in1 = new Input(p);//生产者1线程
Input in2 = new Input(p);//生产者2线程
Output out1 = new Output(p);//消费者1线程
Output out2 = new Output(p);//消费者2线程
Thread t1 = new Thread(in1);
Thread t2 = new Thread(in1);
Thread t3 = new Thread(out1);
Thread t4 = new Thread(out1);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
7.JDK1.5以后的新特性
用Lock lock=new ReentrantLock();
condition conXX=lock.newCondition()
wait -> conXX.await
notify-> conXX.sign
package com.cn.test;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Sourse{
private String name;//资源名称
private int num;//资源数量
private boolean flag=false;//交替旗帜
Lock lock=new ReentrantLock();//新的锁
Condition conPro=lock.newCondition();//能根据不同条件notify(signal)
Condition conCut=lock.newCondition();
public void produce(){
lock.lock();
try {
if(flag){
try {
conPro.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name="货物"+num;
this.num++;
System.out.println("Sheng生产者"+Thread.currentThread().getName()+this.name);
flag=true;
conCut.signalAll();
} catch (Exception e) {
e.printStackTrace();
}
finally{
lock.unlock();//要释放锁
}
}
public void custom(){
lock.lock();
try {
if(!flag){
try {
conCut.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name="货物"+num;
this.num--;
System.out.println("消费者"+Thread.currentThread().getName()+this.name);
flag=false;
conPro.signalAll();
} catch (Exception e) {
e.printStackTrace();
}
finally{
lock.unlock();
}
}
}
class setTread implements Runnable{
private Sourse s;
setTread(Sourse s){
this.s=s;
}
public void run() {
while(true){
s.produce();
}
}
}
class getTread implements Runnable{
private Sourse s;
getTread(Sourse s){
this.s=s;
}
public void run() {
while(true){
s.custom();;
}
}
}
public class For15Demo {
public static void main(String[] args) {
Sourse s=new Sourse();
setTread s1=new setTread(s);
setTread s3=new setTread(s);
getTread s2=new getTread(s);
getTread s4=new getTread(s);
new Thread(s1).start();
new Thread(s2).start();
new Thread(s3).start();
new Thread(s4).start();
}
}
8.其他补充
.interrupt()强制唤醒线程,会抛InterruptedException异常
setPriority(0~10)设置优先级
setDeamXX设置守护线程,在jvm运行环境里只剩下守护线程时,程序结束
join()强行并线