多线程
概述
进程:是一个正在执行中的程序
每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者 叫一个控制单元
线程:是进程中的一个独立的控制单元,线程控制着进程的执行
一个进程中至少有一个线程
java vm(虚拟机) 启动的时候会有一个进程java.exe,该进程中至少有一个线程负责java程序的执行,且这个线程运行的代码存在于main方法中,这个线程称之为主线程
更细节说明虚拟机,jvm启动不止一个线程,还有负责垃圾回收机制的线程
多线程的意义:多线程的出现,可以让程序中的多部分代码同时执行
创建线程_继承Thread类
如何创建线程:
1. 继承Thread类;
2. 复写run();//将自定义的代码储存在run()中,方便线程调用
3. 调用线程的strat();//1. 启动线程 2. 调用run方法
多线程的特性:随机性
为什么要覆盖run():
Thread用于描述线程,该类定义了一个功能用于存储线程要运行的代码,该存储功能就是run()方法
public class Test1 {
public static void main(String[] args){
Demo d1 = new Demo("one+");
d1.start();
Demo d2 = new Demo("two++");
d2.start();
}
}
class Demo extends Thread{
Demo(String name){
System.out.println(name);
}
public void run (){
for(int i = 0; i <= 60; i++ ){
System.out.println(Thread.currentThread().getName() + "::" + i);
}
}
}
线程运行状态
被创建
运行:start()
冻结(睡眠、等待):sleep(time)、wait()、notify()
消亡:stop()、run()方法结束
临时(阻塞)状态:具备运行资格,但是没有执行权
获取线程对象以及名称
获取当前对象:static Thread currentThread()
获取线程名称:getName()
设置线程名称:setNmae()
创建线程_实现Runnable接口
- 实现Runnable接口
- 覆盖Runnable接口中的run()方法
将线程要运行的代码存放在该run()方法中 - 通过Thread类建立对象
- 将Runnable接口的子类对象作为实际参数传递给Thread类的构造参数
- 调用Thread类的start()方法开启线程并调用Runnable接口子类的run()方法
实现方式和继承方式的区别
避免了单继承的局限性,在定义线程的时候建议使用实现方式
两种方法的区别:
继承Thread类:线程运行的代码存放在Thread子类run()方法中
实现Runnable接口:线程运行的代码存放在接口的子类的run()方法中
class Ticket implements Runnable{
private int tick = 100;
Object obj = new Object();
public void run() {
while(true){
if(tick>0){
synchronized(obj){
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName() + "sela:" + tick--);
}
}
}
}
}
public class SellTicketDemo1 {
public static void main(String[] args){
Ticket t = new Ticket();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
多线程的安全问题:
问题原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完另一个线程参与进来执行,导致共享数据的错误
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完再执行过程中,其他线程不能参与执行
同步代码块:
synchronized(对象){
需要被同步的代码
}
参数“对象”就如同锁,持有锁的线程可以在同步中执行
没有持有锁的线程,即使获取了CPU的执行权,也不能执行
同步的前提:
1. 必须要有两个以上的线程
2. 必须是多个线程使用同一个锁
必须保证在同步中有一个线程在运行
好处:解决了多线程的安全问题
弊端:每次都要判断锁,消耗资源
同步函数:
如何找到函数中的多线程同步问题:
1. 明确哪些代码是多线程运行代码
2. 明确共享数据
3. 明确多线程运行代码中哪些语句是操作共享数据的
同步代码块:
Object obj = new Object();
public void test(){
synchronized(obj){
System.out.println("xxxx");
}
}
同步函数:(用synchronized来修饰函数即可)
public synchronized void test(){
System.out.println("xxxx");
}
同步函数用的是哪一个锁:
函数需要被对象调用。那么函数都有一个所属对象引用
就是this,所以同步函数使用的锁是this
同步函数被静态修饰后,使用的锁是什么:
被静态修饰后,不在是this。静态进内存时,内存中没有本类对象,
但是一个顶有该类对应的字节码文件对象。类名.class 该对象的类型是class
静态的同步方法,使用的锁是该方法所在类的字节码文件对象 类名.class
多线程_单例设计模式-懒汉式
饿汉式
class Single{
private static final Single s = new Single();
private Single(){}
//Instance实例
public static Single getInstance(){
return s;
}
}
懒汉式(延迟加载单例设计模式示例)
class Single{
private static Single s = null;
private Single(){}
/* 同步方法后效率会比较低效
public static synchronized Single getInstance(){
if(s == null){
return s;
}
}
*/
public static Single getInstance(){
if(s == null){
synchronized(Single.class){
if(s == null)
s = new Single();
}
}
}
}
懒汉式和饿汉式有什么不同:
1. 懒汉式的特点在于延时加载
2. 懒汉式延时加载在多线程的时候会出现线程安全问题
3. 用同步代码块和同步方法都行,但是会稍微低效,用双重判断可解决效率问题
4. 加同步的时候使用的锁为该类所属的字节码对象
多线程_死锁
同步中嵌套同步,而锁却不同
死锁示例:
public class DeadLockTest {
public static void main(String[] args){
Thread t1 = new Thread(new Test(true));
Thread t2 = new Thread(new Test(false));
t1.start();
t2.start();
}
}
class Test implements Runnable{
private boolean flag;
Test(boolean flag){
this.flag = flag;
}
public void run() {
if(flag){
synchronized(MyLock.locka){
System.out.println("if locka");
synchronized(MyLock.lockb){
System.out.println("if lockb");
}
}
}else{
synchronized(MyLock.lockb){
System.out.println("else lockb");
synchronized(MyLock.locka){
System.out.println("else lockb");
}
}
}
}
}
class MyLock{
static Object locka = new Object();
static Object lockb = new Object();
}
多线程间的通信
多个线程在操作同一个资源,但是操作的动作不同
等待唤醒机制:
public class InputOutputDemo {
public static void main(String[] args){
Res r = new Res();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
class Res{
String name;
String sex;
boolean flag = false;
}
/*
* wait();
* notify();
* notifyall();
* 都是用在同步中,因为要对持有监视器(锁)的线程操作
* 所以要使用在同步中,因为只有同步才具有锁*/
class Input implements Runnable{
private Res r;
Input(Res r){
this.r = r;
}
public void run(){
int x = 0;
while(true){
synchronized(r){
if(r.flag)
try{r.wait();}catch(Exception e){}
if(x == 0){
r.name = "mike";
r.sex = "man";
}else{
r.name = "lili";
r.sex = "woman";
}
x = (x + 1) % 2;
r.flag = true;
r.notify();
}
}
}
}
class Output implements Runnable{
private Res r;
Output(Res r){
this.r = r;
}
public void run(){
while(true){
synchronized(r){
if(!r.flag)
try{r.wait();}catch(Exception e){}
System.out.println(r.name + "..." + r.sex);
r.flag = false;
r.notify();
}
}
}
}
等待唤醒机制代码优化:
public class InputOutputDemo {
public static void main(String[] args){
Res r = new Res();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
}
}
class Res{
private String name;
private String sex;
private boolean flag = false;
public synchronized void set(String name, String sex){
if(flag)
try{this.wait();}catch(Exception e){}
this.name = name;
this.sex = sex;
flag = true;
notify();
}
public synchronized void out(){
if(!flag)
try{this.wait();}catch(Exception e){}
System.out.println(name + "..." + sex);
flag = false;
notify();
}
}
class Input implements Runnable{
private Res r;
Input(Res r){
this.r = r;
}
public void run(){
int x = 0;
while(true){
if(x == 0){
r.set("milk", "man");
}else{
r.set("lili", "women");
}
x = (x + 1) % 2;
}
}
}
class Output implements Runnable{
private Res r;
Output(Res r){
this.r = r;
}
public void run(){
while(true){
r.out();
}
}
}
生产者消费者:
public class ProducerConsumer {
public static void main(String[] args){
Resource r = new Resource();
new Thread(new Producer(r)).start();
new Thread(new Consumer(r)).start();
}
}
class Resource{
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name){
while(flag)
try{wait();}catch(Exception e){}
this.name = name + ".." + count++;
System.out.println(Thread.currentThread().getName() + "...生产者..." + this.name);
flag = true;
this.notifyAll();
}
public synchronized void out(){
while(!flag)
try{wait();}catch(Exception e){}
System.out.println(Thread.currentThread().getName() + "...消费者..." + this.name);
flag = false;
this.notifyAll();
}
}
class Producer implements Runnable{
private Resource res;
Producer(Resource res){
this.res = res;
}
public void run() {
while(true){
res.set("++商品++");
}
}
}
class Consumer implements Runnable{
private Resource res;
Consumer(Resource res){
this.res = res;
}
public void run() {
while(true){
res.out();
}
}
}
生产者消费者JDK1.5升级新特性版本(显式的锁机制):
将同步synchronized替换成现实Lock操作
将Object中的wait,notify,notifyAll,替换Condition对象
该对象可以通过Lock锁进行获取
public class ProducerConsumer {
public static void main(String[] args){
Resource r = new Resource();
new Thread(new Producer(r)).start();
new Thread(new Consumer(r)).start();
new Thread(new Consumer(r)).start();
new Thread(new Consumer(r)).start();
}
}
class Resource{
private String name;
private int count = 1;
private boolean flag = false;
private Lock lock = new ReentrantLock();
private Condition condition_pro = lock.newCondition();
private Condition condition_con = lock.newCondition();
public void set(String name) throws InterruptedException{
lock.lock();
try{
while(flag)
condition_pro.await();
this.name = name + ".." + count++;
System.out.println(Thread.currentThread().getName() + "...生产者..." + this.name);
flag = true;
condition_con.signal();
}finally{
lock.unlock();
}
}
public void out() throws InterruptedException{
lock.lock();
try{
while(!flag)
condition_con.await();
System.out.println(Thread.currentThread().getName() + "...消费者..." + this.name);
flag = false;
condition_pro.signal();
}finally{
lock.unlock();
}
}
}
class Producer implements Runnable{
private Resource res;
Producer(Resource res){
this.res = res;
}
public void run() {
while(true){
try {
res.set("++商品++");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable{
private Resource res;
Consumer(Resource res){
this.res = res;
}
public void run() {
while(true){
try {
res.out();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
多线程_停止线程
stop()已经过时,只有一种run()方法结束
开启多线程,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,结束线程
Thread类中提供interrupt()方法,强制让给线程恢复到运行状态中来
public class StopThreadDemo {
public static void main(String[] args){
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.start();
int num = 0;
while(true){
if(num++ == 60){
t1.interrupt();
t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName() + "......" + num);
}
}
}
class StopThread implements Runnable{
private boolean flag = true;
public synchronized void run(){
while(flag){
try{
wait();
}catch(InterruptedException e){
System.out.println(Thread.currentThread().getName() + "...Exception");
flag = false;
}
System.out.println(Thread.currentThread().getName() + "...run");
}
}
public void changeFlag(){
flag = false;
}
}
多线程_守护线程
- 在线程启动前调用
- 当前台线程全部运行结束后,后台线程自动结束运行(Java虚拟机退出)
setDeamon():
Thread t = new Thread(对象);
t.setDeamon();
t.start();
多线程_Join()方法
当A线程执行到了B线程的.join(),A线程就会等待,等B线程都执行完,A才会执行多线程_优先级 & yield()方法
setPriority(int newPriority);优先级一共10级,10级别最优先
MAX_PRIORITY 10级
MIN_PRIORITY 1级别
NORM_PRIORITY 5级别
Thread.yield();临时释放线程执行权,能达到所有线程平均运行效果
几种多线程的定义方式
public class TestThread {
public static void main(String[] args){
new Thread(){
public void run(){
for(int i=0; i<100; i++){
System.out.println(Thread.currentThread().getName() + "...." + i);
}
}
}.start();
Runnable r = new Runnable(){
public void run(){
for(int i=0; i<100; i++){
System.out.println(Thread.currentThread().getName() + "...." + i);
}
}
};
new Thread(r).start();
for(int i=0; i<100; i++){
System.out.println(Thread.currentThread().getName() + "...." + i);
}
}
}