——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
进程:是一个正在执行中的程序。
每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中的一个独立的控制单元。
线程在控制着进程的执行。
创建线程的方法一:
1.继承Thread类
2.复写Thread类中的run方法
目的是将自定义的代码存储到run方法中,可以让线程执行
3.调用start方法
目的是开启线程,调用run方法
创建线程的方法二:
1.实现Runnable接口
2.复写Runnable接口中的run方法
3.建立Runnable接口的子类对象
4.将Runnable接口的子类对象作为参数传递给Thread类的构造函数
Runnable接口本身并不能创建线程,所以其子类对象并不能调用Thread类中的start方法,即不能开启线程,所以要将其子类对象传递给Thread类,这样才能让线程去运行Runnable接口子类中的run方法
5.调用Thread类中start方法启动线程并运行Runnable接口子类中的run方法
继承Thread类和实现Runnable接口的区别:
实现接口解决了只能单继承的局限性,建议实现接口
区别:
继承Thread类:线程代码存放在Thread类的子类run方法中。
实现Runnable接口:线程代码存放在接口的子类run方法中。
class Demo extends Thread
{
public void run()
{
for (int x = 0;x<50 ;x++ )
{
System.out.println("Demo run......"+x);
}
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Demo d = new Demo();//创建了一个线程
d.start();//开启线程,并执行线程中的run方法
d.run();//只是简单的对象调用方法,没有开启线程
}
}
多线程中常见的安全问题,我们需要通过加锁来解决。
Java对于多线程的安全问题提供了专业的解决方式。
就是同步代码块。
synchronized(对象)
{
需要被同步的代码
}
同步的前提:
1,必须要有两个或者两个以上的线程。
2,必须是多个线程使用同一个锁。
对象如同锁。持有锁的线程可以在同步中执行。
没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁
怎样在代码中解决安全问题:
1.找到多线程所运行的代码 (run方法中所用到的代码)
2.找到共享数据 (几个线程都需要使用的数据)
3.找到操作共享数据的代码,并将其同步
同步函数所使用的锁是 this(函数要被对象所使用,对象有所属的引用就是this)
静态同步函数所使用的锁是该方法所在类的字节码文件对象,即 类名.class
加锁的利弊:
好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源,
//1000张票四个售票员同时卖
class Ticket implements Runnable//extends Thread
{
//private static int x =1000;
private int x = 1000;
Object obj = new Object();
public void run()
{
while(true)
{
//加锁解决安全问题,一定要先确定需要同步的代码
synchronized(obj)
{
if (x>0)
{
try
{
Thread.sleep(10);
}
catch (Exception e)
{
}
System.out.println(Thread.currentThread().getName()+"......."+x--);
}
}
}
}
}
class TicketTest
{
public static void main(String[] args)
{
/*
Ticket t1 = new Ticket();
Ticket t2 = new Ticket();
Ticket t3 = new Ticket();
Ticket t4 = new Ticket();
//开启的四个线程,他们有各自的资源,资源并不共享,可以通过加static的形式让资源共享
t1.start();
t2.start();
t3.start();
t4.start();
*/
Ticket t = new Ticket();
//传入的是同一个对象,所以资源共享
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
线程间的相互通信:
其实就是多个线程在操作同一个资源,但是操作的动作不同。
例:
生产者消费者,生产一个消费一个并存在多个生产者和消费者
思路:
1.定义一个资源,它既具有生产的方法同时具有消费的方法
2.定义一个生产者实现Runnable接口,同时在run函数中调用资源中的生产方法
3.定义一个消费者实现Runnable接口,在run函数中调用资源中的消费方法
4.开启并运行生产和消费的线程
要想让生产者和消费者有关系并且生产一个就消费一个,可以通过标记和等待唤醒机制来完成
class Resource
{
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void produce(String name)
{
while (flag)
{
try
{
//如果标记为true,就会等待
this.wait();
}
catch (Exception e)
{
}
}
this.name = name+"..."+count++;
System.out.println(Thread.currentThread().getName()+"..生产者.."+this.name);
//生产一个商品后,就将标记改为true,这样生产者就会等待,然后唤醒所有的线程
flag = true;
this.notifyAll();
}
public synchronized void consume()
{
while (!flag)
{
try
{
//如果标记为false就会等待
this.wait();
}
catch (Exception e)
{
}
}
System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name);
//消费完后,改变标记,唤醒所有线程
flag = false;
this.notifyAll();
}
}
class Producer implements Runnable
{
Resource res ;
Producer(Resource res)
{
this.res = res ;
}
public void run()
{
while (true)
{
res.produce("旺旺");
}
}
}
class Consumer implements Runnable
{
Resource res ;
Consumer(Resource res)
{
this.res = res ;
}
public void run()
{
while (true)
{
res.consume();
}
}
}
class ProducerConsumerDemo
{
public static void main(String[] args)
{
//同一个对象资源共享
Resource res = new Resource();
/*
Producer pro = new Producer(res);
Consumer con = new Consumer(res);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
*/
//创建并开启四个线程,两个为生产者,两个为消费者
new Thread(new Producer(res)).start();
new Thread(new Producer(res)).start();
new Thread(new Consumer(res)).start();
new Thread(new Consumer(res)).start();
}
}
停止线程:
特殊情况:当线程处于冻结状态,就不会读到标记,那么线程就不会结束
为了让线程结束,Thread类提供了一个interrupt方法,可以强制让线程从冻结中醒来,但会报出一个InterruptedException
class Interrupte implements Runnable
{
//定义一个标记
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");
}
}
}
class InterrupteDemo
{
public static void main(String[] args)
{
Interrupte in = new Interrupte();
Thread t1 = new Thread(in);
Thread t2 = new Thread(in);
t1.start();
t2.start();
int x = 0 ;
while (true)
{
if (x++ == 50)
{
t1.interrupt();//强制打断t1进程
t2.interrupt();//强制打断t1进程
break;
}
System.out.println("main....run......"+x);
}
}
}
守护线程:
void setDaemon(boolean on)
当传入的值为true时,为守护线程,当前台线程都结束时,守护线程也结束,线程jvm退出。
在启动线程前调用,主线程结束,守护线程结束。
多线程的join方法
void join()
当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。
join可以用来临时加入让指定线程执行。
多线程的setPriority方法
void setPriority(int newPriority)
用于更改线程的优先级。
多线程的yield方法
static void yield()
暂停当前正在执行的线程对象,并执行其他线程。