------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
线程
概念:线程是指进程中的执行流程,一个进程中可以运行多个线程。
线程总体分为:守护线程、用户线程。
线程的状态:生、死、可运行、运行、等待/阻塞。
多线程好处、弊端
好处:提高程序的效率,可以同时完成多项工作。
弊端:多线程共执行的代码块容易出现安全隐患。
1.创建线程方式
-
1.继承Thread类。
-
好处:直接继承Thread类中的方法,代码简单易懂。
-
弊端:如果此类有了父类,就使用不了这种方法,类不能多继承。
-
1.1定义类继承Thread。
-
1.2重写run方法。
-
1.3把新线程要做的事写在run方法中。
-
1.4创建线程对象。
-
1.5开启新线程,内部会自动执行run方法。
package ThreadDemo;
class MyThread extends Thread
{
public void run()
{
while(true)
{
System.out.println("你好!");
}
}
}
public class MyThreadDemo
{
public static void main(String[] args)
{
MyThread t = new MyThread();
t.start();
}
}
-
2.实现Runnable接口
-
好处:定义的线程类有了父类,此法方法一样可以实现。
-
弊端:需要先获取线程对象,才能得到Thread的方法,过程比较复杂。
-
2.1定义类实现Runnable接口。
-
2.2实现run方法。
-
2.3把新线程要做的事写在run方法中。
-
2.4创建自定义的Runnable的子类对象。
-
2.5创建Thread对象,传入Runnable。
-
2.6调用start()开启新线程,内部会自动调用Runnable的run()方法。
package ThreadDemo;
class MyThread implements Runnable
{
public void run()
{
while(true)
{
System.out.println("hello");
}
}
}
public class MyThreadDemo
{
public static void main(String[] args)
{
MyThread t = new MyThread();
new Thread(t).start();
}
}
2Threa类常用方法
- 1.获取名字:通过getName()方法获取线程对象的名字。
- 2.设置线程名字:通过构造函数传入String类型的名字例如:new Thread("线程名字")。
- 3.获取当前线程对象:Thread.currentThread()(此方法主线程也可以获取)。
- 4.休眠:通过Thread.sleep(毫秒、纳秒),控制当前线程休眠若干毫秒。
- 5.守护:通过setDaemon设置一个线程为守护线程。当其他非守护线程都执行结束后,自动退出。
- 6.加入:join(),当前线程暂停,等待指定的线程执行结束后, 当前线程再继续。join(int), 可以等待指定的毫秒之后继续。
3线程同步
当有线程操作都操作多段代码块时,容易出现安全问题,所以希望执行这段代码的时候只有一个线程,当这个线程执行完后,其他线程才能来执行这个多段代码块,这时就需要同步这代码块。
1synchronized同步
1.1同步代码块
使用synchronized关键字加上一个锁对象来定义一段代码。
package ThreadDemo;
class Resouce
{
int count = 100;
public void out()
{
synchronized(this)
{
System.out.println(count);
count--;
}
}
}
class MyThread implements Runnable
{
private Resouce res;
public MyThread(Resouce res)
{
this.res = res;
}
public void run()
{
while(true)
{
res.out();
}
}
}
public class MyThreadDemo
{
public static void main(String[] args)
{
Resouce res = new Resouce();
MyThread t1 = new MyThread(res);
MyThread t2 = new MyThread(res);
new Thread(t1).start();
new Thread(t2).start();
}
}
1.2同步方法
使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的。
package ThreadDemo;
class Resouce
{
int count = 100;
public synchronized void out()
{
System.out.println(count);
count--;
}
}
class MyThread implements Runnable
{
private Resouce res;
public MyThread(Resouce res)
{
this.res = res;
}
public void run()
{
while(true)
{
res.out();
}
}
}
public class MyThreadDemo
{
public static void main(String[] args)
{
Resouce res = new Resouce();
MyThread t1 = new MyThread(res);
MyThread t2 = new MyThread(res);
new Thread(t1).start();
new Thread(t2).start();
}
}
2使用ReentrantLock类同步(JDK5后)
package ThreadDemo;
import java.util.concurrent.*;
class Resouce
{
private String sThreadName;
private int nCount = 1;
private Boolean bFlag = false;
private Lock lock= new ReentrantLock();
private Condition pro = lock.newCondition();
private Condition con = lock.newCondition();
void set(String name) throws InterruptedException
{
lock.lock();
try
{
while(bFlag)
{
pro.await();
}
this.sThreadName = name+"--"+nCount++;
System.out.println(Thread.currentThread()+"生产"+"---"+this.sThreadName);
bFlag = true;
con.signal();
}
finally
{
lock.unlock();
}
}
void out() throws InterruptedException
{
lock.lock();
try
{
while(!bFlag)
{
con.await();
}
System.out.println(Thread.currentThread()+"***消费"+"---"+this.sThreadName);
bFlag = false;
pro.signal();
}
finally
{
lock.unlock();
}
}
}
class Consumer implements Runnable
{
private Resouce res;
Consumer(Resouce res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.out();
}
catch (Exception e)
{
System.out.println("消费商品失败!");
}
}
}
}
class Produce implements Runnable
{
private Resouce res;
public Produce(Resouce res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.set("商品");
}
catch (Exception e)
{
System.out.println("生产商品失败!");
}
}
}
}
public class ThreadDemo2
{
public static void main(String[] args)
{
Resouce res = new Resouce();
Produce p = new Produce(res);
Consumer c = new Consumer(res);
Thread t1 = new Thread(p);
Thread t2 = new Thread(p);
Thread t3 = new Thread(c);
Thread t4 = new Thread(c);
Thread t5 = new Thread(c);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
4死锁问题
使用同步嵌套,使用了相同锁,就可能出现死锁。
package ThreadDemo;
class MyThread implements Runnable
{
private Boolean flag;
public MyThread()
{}
public MyThread(Boolean flag)
{
this.flag = flag;
}
public void run()
{
while(true)
{
if(flag)
{
synchronized (MyThreadDemo.A)
{
System.out.println("if-A锁");
synchronized (MyThreadDemo.B)
{
System.out.println("if-B锁");
}
}
}
else
{
synchronized (MyThreadDemo.B)
{
System.out.println("else-B锁");
synchronized (MyThreadDemo.A)
{
System.out.println("else-A锁");
}
}
}
}
}
}
public class MyThreadDemo
{
static String A = "a";
static String B = "b";
public static void main(String[] args)
{
MyThread t1 = new MyThread(true);
MyThread t2 = new MyThread(false);
new Thread(t1).start();
new Thread(t2).start();
}
}
5多线程通信
多线程并发执行时,我们控制线程的有规律的执行(默认是CPU随机切换执行),就可以使用通信。
5.1JDk5以前
-
1.wait():线程等待。
-
2.notify():随机唤醒一个线程。
-
3.notifyAll():唤醒所有线程。
5.2JDk5以后
-
1.使用ReentrantLock类的newCondition()方法可以获取Condition对象。
-
2.需要等待线程的时候使用Condition的await()方法, 需要被唤醒的线程时候用Conditon的signal()方法。
-
3.不同的线程使用不同的Condition, 这样就能区分唤醒的时候找哪个线程了。
6.线程池(JDK5后)
线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。
线程池的常见类型:固定尺寸的线程池、可变尺寸的连接池。
固定尺寸线程池
package ThreadDemo;
import java.util.concurrent.*;
class MyThread implements Runnable
{
public void run()
{
System.out.println(Thread.currentThread().getName()+"执行了!");
}
}
public class ExService
{
public static void main(String[] args)
{
MyThread t = new MyThread();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
Thread t5 = new Thread(t);
ExecutorService pool = Executors.newFixedThreadPool(2); //创建固定大小的线程池。
pool.execute(t1); //把线程加入到线程池中。
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
pool.shutdown();
}
}
单任务线程池
只需要把pool的创建方式ExecutorService pool = Executors.newFixedThreadPool(2);改成
ExecutorService pool = Executors.newSingleThreadExecutor(); //创建固单个线程执行的线程池。
可变尺寸的线程池
只需要把pool的创建方式ExecutorService pool = Executors.newCachedThreadPool();
延迟连接池
线程在给定的延迟后运行命令,或者定期的执行
package ThreadDemo;
import java.util.concurrent.*;
class MyThread implements Runnable
{
public void run()
{
System.out.println(Thread.currentThread().getName()+"执行了!");
}
}
public class ExService
{
public static void main(String[] args)
{
MyThread t = new MyThread();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
Thread t5 = new Thread(t);
ScheduledExecutorService pool = Executors.newScheduledThreadPool(2); //创建延迟线程执行的线程池。
pool.execute(t1); //把线程加入到线程池中。
pool.schedule(t3, 1, TimeUnit.MINUTES); //线程延迟1分钟执行。
pool.execute(t2);
pool.execute(t4);
pool.execute(t5);
pool.shutdown();
}
}
自定义线程池
package ThreadDemo;
import java.util.concurrent.*;
class MyThread implements Runnable
{
public void run()
{
System.out.println(Thread.currentThread().getName()+"执行了!");
}
}
public class ExService
{
public static void main(String[] args)
{
MyThread t = new MyThread();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
Thread t5 = new Thread(t);
BlockingQueue<Runnable> bque = new ArrayBlockingQueue<Runnable>(20);
ThreadPoolExecutor poo = new ThreadPoolExecutor(2, 3,2, TimeUnit.MICROSECONDS, bque);
ScheduledExecutorService pool = Executors.newScheduledThreadPool(2); //创建固单个线程执行的线程池。
pool.execute(t1); //把线程加入到线程池中。
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
pool.shutdown();
}
}
创建自定义线程池的构造方法很多,ThreadPoolExecutor就是其中一个,这个方法的蚕食含义如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
参数:
-
corePoolSize -池中所保存的线程数,包括空闲线程。
-
maximumPoolSize -池中允许的最大线程数。
-
keepAliveTime -当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
-
unit - keepAliveTime参数的时间单位。
-
workQueue -执行前用于保持任务的队列。此队列仅保持由execute方法提交的Runnable任务。