目录
创建多线程第一种方法:
package Multiple__Thread;
/*
* 进程:是程序一次的运行
* 多线程:是一个进程里的多个线程进行操作
*
* 创建的第一种方法:继承Thread类,重写run方法
* Thread有以下方法:
*
* start():开始线程
* sleep(long millis):休眠当前正在执行线程,millis为休眠毫秒数
* join():老大加入(A线程中B线程调用join()方法,必须等B执行完后才能执行A线程)
* yield():暂停当前正在执行线程,可能又被当前线程抢占CPU,也可能被其它线程抢占CPU
* interrupt():从等待状态中退出
* isAlive():判断当前线程是否存在
*
* currentThread():返回当前线程(静态方法)
* setName(String):设置线程名字
* getName():返回线程名字
*
* setPriority(int newPriority):设置线程优先级(默认为5,最低1,最高10)
* getPriority():返回当前线程优先级
*/
//创建一个子线程,和主线程一起输出1-100的正整数
class MyThread extends Thread{
public void run() {
for(int i=1;i<=100;i++) {
try {
Thread.currentThread().sleep(1000); //执行时休眠1000毫秒
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+Thread.currentThread().getPriority()+":"+i);
}
}
}
public class CreateWay {
public static void main(String[] args) throws InterruptedException {
MyThread m=new MyThread();
//m.run(); //错误:若此时执行run()只是调用对象的run方法,不会创建一个线程
m.start();
//修改主线程,子线程名称
m.setName("子线程");
Thread.currentThread().setName("========主线程");
if(m.isAlive()) {System.out.println("子线程未执行完毕");} //判断子线程是否存在
//主线程
Thread.currentThread().setPriority(10); //设置优先级后,明显主线程抢占CPU较快
for(int i=1;i<=100;i++) {
if(i%10==0) {Thread.currentThread().yield();} //当i为10的倍数时,看看主线程子线程哪个能否抢占CPU
System.out.println(Thread.currentThread().getName()+Thread.currentThread().getPriority()+":"+i);
if(i==80) {m.join();} //子线程变大佬加入,只能等大佬执行完后主线程再执行
}
if(!m.isAlive()) {System.out.println("子线程已执行完毕");}
}
}
创建多线程第二种方法:
package Multiple__Thread;
/*
* 多线程的另一方式(也是常用方式):接口Runnable提供了run()方法,且只有run()方法
* 优点:
* 1、当线程类已经继承于某个类,但是仍然想实现多线程可用此方式
* 2、多个线程能共享一个对象的资源(在Thread方式里要设置为静态变量)
*
* 本质(总结):只有Thread类才能实现线程,要么继承它,要么利用Thread的构造函数与Runnable接口进行复制
*/
//既继承了基类,又实现了接口Runnable的run()方法
class People{
}
class Student extends People implements Runnable{
public void run() {
System.out.println("我是学生类");
}
public void show() {
System.out.println("hhh");
}
}
//共享资源例子(继承Thread方式):火车票分四个窗口售出
class WindowThread extends Thread{
int ticket=100;
//static int ticket=100; //当不设置为static变量时达不到共享一个类资源的效果,并且设置成static消耗较大,jvm不会回收
public void run() {
while(ticket>0)
System.out.println(Thread.currentThread().getName()+"窗口正在售出第"+(ticket--)+"张票");
}
}
//共享资源例子(实现Runnable接口方式):火车票分四个窗口售出
class Window implements Runnable{
private int ticket=100;
public void run() {
while(ticket>0) {
System.out.println(Thread.currentThread().getName()+"窗口正在售出第"+(ticket--)+"张票");
}
}
}
public class CommonCreateWay {
public static void main(String[] args) {
//设置四个窗口
/*WindowThread w1=new WindowThread();
WindowThread w2=new WindowThread();
WindowThread w3=new WindowThread();
WindowThread w4=new WindowThread();
w1.start();w2.start();w3.start();w4.start();*/
Window w=new Window(); //设置一个总窗口
Thread t=new Thread(w,"窗口1"); //用四个Thread线程构造四个窗口
t.start();
t=new Thread(w,"窗口2");
t.start();
t=new Thread(w,"窗口3");
t.start();
t=new Thread(w,"窗口4");
t.start();
}
}
多线程同步:
package Multiple__Thread;
/*
* 线程有几个状态:出生态(刚被创建出来),就绪态(调用start()方法后开始抢夺CPU资源),运行态(正在被运行的线程),死亡态(结束线程)
* 阻塞态(执行一半被搁置的状态):调用wait(),join(),sleep()等出现状态
*
* 当线程进入阻塞态时,可能就会引起线程安全问题。
* 这时就需要一个同步机制:
* 一、同步代码块:当操作共享资源时,突然进入阻塞状态会引起安全问题,就需要同步代码块使得一个线程执行完毕后
* 另一线程才能执行。
* 格式:synchronized(任意对象){ //一般称此对象为锁,锁住共享资源只能供一人使用
* 同步的代码块(一般为操作共享资源的代码块)
* }
*
* 二、同步方法:锁为当前对象(this)
* 格式:权限 synchronized 返回值 方法名()
* }
*
* 单例模式:属于工厂模式的特例,只是它不需要输入参数并且始终返回同一对象的引用(即某类在系统中只有一个实例)
* 饿汉式:在程序启动或单件模式类被加载的时候,单例模式实例就已经被创建。
* 懒汉式:当程序第一次访问单例模式实例时才进行创建
* 若该对象经常被访问使用饿汉式,若该对象不经常被访问使用懒汉式。
*/
//进入阻塞状态后出现的错误
class DangerousWindow implements Runnable{
private int ticket=100;
public void run() {
while(ticket>0) {
try {
Thread.sleep(15);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"窗口正在售出第"+(ticket--)+"张票");
}
}
}
class SatefyWindow implements Runnable{
private int ticket=100;
Object obj=new Object();
public void run() {
//同步代码块
while(true) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized(obj) { //若同步放在while上面即函数开始,会导致只有一个窗口能售票,因为
//其它窗口刚想售票时(抢夺CPU时)共享资源和CPU会马上被锁住
if(ticket>0) {
System.out.println(Thread.currentThread().getName()+"窗口正在售出第"+(ticket--)+"张票");
}
else {break;}
}
}
//同步方法
/*while(true) {
try {
Thread.sleep(15);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sale();
}*/
}
public synchronized void sale() {
if(ticket>0) {
System.out.println(Thread.currentThread().getName()+"窗口正在售出第"+(ticket--)+"张票");
}
}
}
//饿汉式
class Hungry_Single{
private static Hungry_Single single=new Hungry_Single();
public static Hungry_Single get_Single() {
return single;
}
}
//懒汉式(容易引起多线程问题,要使用同步机制)
class Lazy_Single{
private static Lazy_Single single=null;
public static Lazy_Single get_Single() {
if(single==null) { //让其它线程知道接下来是一个同步代码块,不为空时不用跟着浪费时间,不用处于等待态
//静态方法里没法使用this,所以使用Lazy_Single.class,一样效果
synchronized(Lazy_Single.class) {
if(single==null) {
single=new Lazy_Single();
}
}
}
return single;
}
}
//练习题目:银行有一个账户,有俩个人分别向同一账户存3000元,每次存1000,分三次,每次存完打印账户余额
/*
* 分析: 是否有多线程?有,俩个人。
* 是否有共享资源?有,一个账户。
* 是否需要同步机制?需要,当其中一个人操作账户时其它人不能操作
*/
class Account{
protected float money=0;
public void save_money(float money) {this.money+=money;
System.out.println("当前账户余额:"+this.money);}
public float get_money() {return money;}
}
class Custom implements Runnable{
Account a;
Custom(Account a){this.a=a;}
public synchronized void run() {
//当第一线程刚执行完存钱操作后,第二线程又马上进行判断和存钱操作,会引起错误。
while(a.get_money()<3000) //当余额高于3000时不必执行以下操作
{
a.save_money(1000);
}
}
}
public class synchronized_ {
public static void main(String args[]) {
//DangerousWindow w=new DangerousWindow(); //设置一个总窗口
SatefyWindow w=new SatefyWindow(); //设置一个总窗口
Thread t=new Thread(w,"窗口1"); //用四个Thread线程构造四个窗口
t.start();
t=new Thread(w,"窗口2");
t.start();
t=new Thread(w,"窗口3");
t.start();
t=new Thread(w,"窗口4");
t.start();
//测试饿汉懒汉
/*Hungry_Single h1=Hungry_Single.get_Single();
Lazy_Single h2=Lazy_Single.get_Single();*/
//练习题目测试
/*Account account=new Account();
Custom c=new Custom(account);
Thread t1=new Thread(c);
Thread t2=new Thread(c);
t1.start();t2.start();*/
}
}
多线程通信:
package Multiple__Thread;
/*
* 死锁:是指俩个线程互相有依赖状态,各自进入阻塞态时都在等待对方的锁
* 避免死锁:1、同步区域拥有各自的锁2、同步代码块用完操作资源后立马释放
*
* 多线程通信(合作):当多个线程需要协作操作同一内存资源(类)时,必须使用通信机制,防止某一线程占用CPU太多出现的问题
* 通信机制有以下方法:
* wait():使当前线程进入阻塞态,并释放锁。
* notify():通知该锁第一个调用wait()线程苏醒(可被其它线程通知,实现一个通信效果)
* notifyAll():通知该锁所有调用wait()线程苏醒(可被其它线程通知,实现一个通信效果)
* 注意: 1、以上方法必须和while搭配使用,保证安全性,防止在wait()之前notify()就来通知苏醒
* 2、以上方法的调用对象必须是同步锁对象,否则就会引起异常
* 3、以上方法必须在try catch里使用
* wait()和sleep()和yield()的区别:yield()是释放CPU当前执行线程,使线程进入就绪态
* sleep()是使当前CPU执行线程进入阻塞态,wait()是使当前CPU执行线程进入阻塞态,并释放锁(即此时其它线程可进入同步代码)。
*/
//多线程通信
class Storage{
protected int number[]=new int[20];
protected int putcount=0,getcount=0; //记录进出数组数据量
protected int size=0; //记录当前数组的规模(数据数),某一线程过快,每放入或取出数据
//达到数组规模(满或空状态)时休眠一次
Object putlock=new Object();
Object getlock=new Object();
synchronized void put(int num) {
try {
while(size==number.length) {
this.wait(); //当前数组规模已达容量值,休眠线程
}
if(putcount==number.length) {
putcount=0; //达到一个循环数组的效果
}
number[putcount++]=num;
size++; //更新规模
System.out.println("向数组放入第"+size+"个元素:"+num);
this.notify(); //从while循环退出后,规模不为满,可以唤醒线程
}catch(Exception e) {
e.printStackTrace();
}
}
synchronized int get() {
int temp = 0;
try {
while(size==0) {
this.wait(); //当前数组规模已为0,线程休眠
}
if(getcount==number.length) {
getcount=0; //达到一个循环数组的效果
}
temp=number[getcount];
getcount++;
size--; //更新规模
System.out.println("从数组取出第"+size+"个元素:"+temp);
this.notify(); //从while循环退出后,规模不为空,可以唤醒线程
}catch(Exception e) {
e.printStackTrace();
}
return temp;
}
}
class put_Thread implements Runnable{
private Storage st;
put_Thread(Storage st){this.st=st;}
public synchronized void run() {
while(true) {
st.put(0);
}
}
}
class get_Thread implements Runnable{
private Storage st;
get_Thread(Storage st){this.st=st;}
public synchronized void run() {
while(true) {
st.get();
}
}
}
public class deadlock_cooperation {
//死锁例子:美国人和中国人收集工具,美国人进入阻塞态需要筷子,中国人进入阻塞态需要刀
static class deadlock implements Runnable{
private Object chopstickslock=new Object();
private Object knifelock=new Object();
boolean judge;
deadlock(boolean judge){this.judge=judge;}
public void run() {
if(judge) {
while(true) {
synchronized(chopstickslock) {
System.out.println("中国人已有筷子");
//因CPU执行速度太快,必须暂缓一会模拟死锁
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized(knifelock) {
System.out.println("中国人已有筷子已有刀叉");
}
}
}
}
else {
while(true) {
synchronized(knifelock) {
System.out.println("美国人已有刀叉");
//因CPU执行速度太快,必须暂缓一会模拟死锁
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized(chopstickslock) {
System.out.println("美国人已有刀叉已有筷子");
}
}
}
}
}
}
public static void main(String[] args) {
//测试死锁
/*deadlock China=new deadlock(true);
deadlock America=new deadlock(false);
Thread Copen=new Thread(China);
Thread Aopen=new Thread(America);
Copen.start();Aopen.start();*/
//测试通信
Storage s=new Storage(); //s即为同一块内存空间
put_Thread put=new put_Thread(s); //放入线程通过构造方式操纵同一块内存空间(类s)
get_Thread get=new get_Thread(s); //取出线程通过构造方式操纵同一块内存空间(类s)
Thread t1=new Thread(put);
Thread t2=new Thread(get);
t1.start();t2.start();
}
}
/*
* 通信结果:当放入至20个元素时,线程等待,当取出线程已取完时,取出线程等待
向数组放入第1个元素:0
向数组放入第2个元素:0
向数组放入第3个元素:0
向数组放入第4个元素:0
向数组放入第5个元素:0
向数组放入第6个元素:0
向数组放入第7个元素:0
向数组放入第8个元素:0
向数组放入第9个元素:0
向数组放入第10个元素:0
向数组放入第11个元素:0
向数组放入第12个元素:0
向数组放入第13个元素:0
向数组放入第14个元素:0
向数组放入第15个元素:0
向数组放入第16个元素:0
向数组放入第17个元素:0
向数组放入第18个元素:0
向数组放入第19个元素:0
向数组放入第20个元素:0
从数组取出第19个元素:0
从数组取出第18个元素:0
从数组取出第17个元素:0
从数组取出第16个元素:0
从数组取出第15个元素:0
从数组取出第14个元素:0
从数组取出第13个元素:0
从数组取出第12个元素:0
从数组取出第11个元素:0
从数组取出第10个元素:0
从数组取出第9个元素:0
从数组取出第8个元素:0
从数组取出第7个元素:0
从数组取出第6个元素:0
从数组取出第5个元素:0
从数组取出第4个元素:0
从数组取出第3个元素:0
从数组取出第2个元素:0
从数组取出第1个元素:0
从数组取出第0个元素:0
向数组放入第1个元素:0
向数组放入第2个元素:0
向数组放入第3个元素:0
向数组放入第4个元素:0
向数组放入第5个元素:0
*/