1、线程
1.1 线程的基本概念
线程是独立的执行路径,多线程就是多条独立执行的路径。
1.2 程序、进程、线程的关系
- 程序就是代码。
- 进程就是指在操作系统中运行的程序。
- 一个进程中有多个线程,在程序运行中,即使没有自己创建线程,后台也会存在多个线程,如:gc线程,主线程。
2、多线程线程创建的方式
2.1 继承Thread类
//创建线程:继承Thread + 重写run()方法
public class TestThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("唱歌");
}
}
public static void main(String[] args) {
//启动线程:创建子类对象 + 调用start()方法
TestThread thread = new TestThread();
thread.start();
// thread.run(); //调用普通方法
for (int i = 0; i < 20; i++) {
System.out.println("学习");
}
}
}
实例:下载图片
工具类:
public class DownloaderUtils {
public void utils(String url,String name) {
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (MalformedURLException e) {
e.printStackTrace();
System.out.println("不合法的url");
} catch (IOException e) {
e.printStackTrace();
System.out.println("图片下载失败");
}
}
}
多线程实现:
public class TestDownloader extends Thread {
String url;
String name;
public TestDownloader(String url, String name) {
super();
this.url = url;
this.name = name;
}
@Override
public void run() {
DownloaderUtils du = new DownloaderUtils();
du.utils(url, name);
System.out.println(name);
}
public static void main(String[] args) {
TestDownloader td1 = new TestDownloader("http://pic16.nipic.com/20111006/6239936_092702973000_2.jpg", "tu1.jpg");
TestDownloader td2 = new TestDownloader("http://photocdn.sohu.com/20120128/Img333056814.jpg", "tu2.jpg");
TestDownloader td3 = new TestDownloader("http://pic27.nipic.com/20130315/11511914_151013608193_2.jpg", "tu3.jpg");
//启动线程
td1.start();
td2.start();
td3.start();
}
}
2.2 实现Runnable接口
- 避免了单继承的局限性,方便共享资源,优先使用。
//创建线程:继承Thread + 重写run()方法
public class TestRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("唱歌");
}
}
public static void main(String[] args) {
//启动线程:创建实现类对象 + 创建代理类对象 + 调用start()方法
// TestRunnable tr = new TestRunnable();
// Thread t = new Thread(tr);
// t.start();
new Thread(new TestRunnable()).start();
for (int i = 0; i < 20; i++) {
System.out.println("学习");
}
}
}
TestRunnable tr = new TestRunnable();
Thread t = new Thread(tr);
t.start();
||
||
new Thread(new TestRunnable()).start();
实例:12306抢票
public class Test12306 implements Runnable{
private int ticketNums = 99;
@Override
public void run() {
while(true) {
if (ticketNums < 0) {
break;
}
System.out.println(Thread.currentThread().getName() + "--->" + ticketNums--);
}
}
public static void main(String[] args) {
Test12306 test = new Test12306();
new Thread(test,"张三").start();
new Thread(test,"李四").start();
new Thread(test,"王五").start();
}
}
2.3 实现Callable接口(了解)
3、线程状态
3.1 线程终止的方法
- 方法1:有限定次数:线程执行完毕。
- 方法2:外部干涉:加入标识。
- 不要使用stop、destory,不安全。
3.2 sleep(暂停/休眠)
- sleep(时间):指定当前线程阻塞的毫秒数。
- sleep进入阻塞状态,时间到达后进入就绪状态。
- sleep不会释放锁。
- sleep可以模拟网络延时、倒计时等。
/*
* 模拟倒计时
*/
public class TestSleep {
public static void main(String[] args) throws InterruptedException {
Date endTime = new Date(System.currentTimeMillis() + 1000*10);
long end = endTime.getTime();
while(true) {
System.out.println(new SimpleDateFormat("mm:ss").format(endTime));
Thread.sleep(1000);
endTime = new Date(endTime.getTime()-1000);
if (end-10000 > endTime.getTime()) {
break;
}
}
}
}
3.3 yield(礼让)
- 礼让线程,让当前正在执行的线程暂停。
不是阻塞线程
,而是将线程从运行状态转入就绪状态,重新进入调度器等待CPU调度。
3.4 join(插队)
- join合并线程,待此线程执行完成后,再执行其他线程(其他线程阻塞)。
4、线程优先级
线程的优先级:1-10
-
NORM_PRIORITY:默认为5
-
MAX_PRIORITY:10
-
MIN_PRIORITY:1
优先级代表一个概率问题,不代表绝对的先后顺序。
public class TestPriority {
public static void main(String[] args) {
MyPriority myPriority = new MyPriority();
Thread t1 = new Thread(myPriority,"aa");
Thread t2 = new Thread(myPriority,"bb");
Thread t3 = new Thread(myPriority,"cc");
Thread t4 = new Thread(myPriority,"dd");
Thread t5 = new Thread(myPriority,"ee");
Thread t6 = new Thread(myPriority,"ff");
//设置优先级
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MAX_PRIORITY);
t3.setPriority(Thread.MAX_PRIORITY);
t4.setPriority(Thread.MIN_PRIORITY);
t5.setPriority(Thread.MIN_PRIORITY);
t6.setPriority(Thread.MIN_PRIORITY);
//启动线程
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "--->" + Thread.currentThread().getPriority());
Thread.yield();
}
}
- 默认情况下,JVM要等待用户线程执行完毕才会停止,使用
守护线程(Daemon)
后,JVM不用等待守护线程执行完毕。
5、线程同步
- 并发:同一个对象多个线程同时执行。
- 线程同步的两个条件:(1)形成队列;(2)锁机制(
synchronized
)。 - 一个线程持有所会导致其他所有需要此锁的线程挂起。
- 线程安全:在并发时保证数据的正确性,尽量提高效率。
5.1 线程同步的实现
- synchronized方法:会影响程序的性能。
public synchronized void method(int args){
}
- synchronized块:目标更明确。
synchronized(args){
}
实现线程同步的关键:加锁的位置要加对。
5.2 死锁
- 过多的同步可能会造成相互不释放资源,从而相互等待造成死锁,一般发生于同步中持有多个对象的锁。
- 不要在同一个代码块中,同时持有多个对象的锁。
5.3 生产者消费者模式
- wait():表示线程一直等待,直到其他线程通知。会释放锁。
- notifiy():唤醒一个处于等待状态的线程。
- notifiyAll():唤醒同一个对象上所有调用wait()方法的线程。