本文共 8578 字,大约阅读时间需要 28 分钟。
程序是指令和数据的有序集合,是静态的概念;进程是程序的一次执行过程,是一个动态的概念,是系统资源分配的单位;线程是CPU调度和执行的单位。
注意:很多多线程是模拟出来的,真正的多线程是指有多个CPU,即多核。
在程序运行时,即使自己没有创建线程,后台也会有多个线程,如主线程、gc线程。
对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制。
为了创建线程,可以通过继承Thread类并实现run()方法。以下是一个实现的示例:
public class TestThread2 extends Thread { private String url; private String name; public TestThread2(String url, String name) { this.url = url; this.name = name; } @Override public void run() { WebDownloader downloader = new WebDownloader(); downloader.download(url, name); System.out.println("下载了文件名为:" + name); } public static void main(String[] args) { TestThread2 t1 = new TestThread2("https://pics7.baidu.com/feed/0d338744ebf81a4cbf5a41d981e0105e242da68c.jpeg?token=760f959b12c1adc6be805c125a569b6b", "1.jpg"); TestThread2 t2 = new TestThread2("https://pics3.baidu.com/feed/9358d109b3de9c8231a97ed1ca4fef0d1bd843f2.jpeg?token=766bf62b302cb0d56fb24c4469bb57a2", "2.jpg"); TestThread2 t3 = new TestThread2("https://pics3.baidu.com/feed/314e251f95cad1c8773c1c6ddaf0080ecb3d51cf.jpeg?token=42e2511b4151fcf2d648be6eff246e1d", "3.jpg"); t1.start(); t2.start(); t3.start(); }} 线程可以通过实现Runnable接口来创建。Runnable接口提供一个run()方法,线程实现该方法即可。以下是一个实现的示例:
public class TestThread3 implements Runnable { public void run() { for (int i = 0; i < 10; i++) { System.out.println("我在看第-" + i + "-行代码"); } } public static void main(String[] args) { TestThread3 testThread3 = new TestThread3(); new Thread(testThread3).start(); }} 注意:Thread类实现了Runnable接口,因此可以用Thread类的方式创建线程。
创建线程的核心方法有两种方式:继承Thread类和实现Runnable接口。无论使用哪种方式,线程的创建和运行流程是相同的。
线程的停止通常可以通过 호출stop()方法实现,但stop()方法可能导致线程中断当前的休眠状态。此外,也可以通过标志控制线程的执行状态。
public class TestStop implements Runnable { private boolean flag = true; public void run() { while (flag) { System.out.println("run...Thread"); } } public void stop() { this.flag = false; } public static void main(String[] args) { TestStop task = new TestStop(); new Thread(task).start(); }} 线程的休眠可以通过调用sleep()方法实现,sleep()方法会让线程暂停执行。但sleep()方法不会释放同步锁。
public class TestSleep implements Runnable { public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { TestSleep task = new TestSleep(); new Thread(task).start(); }} 线程可以通过调用yield()方法进行礼让,使当前线程暂时离开CPU,从而给其他线程让行。
public class TestYield implements Runnable { public void run() { while (true) { System.out.println("running... Thread"); Thread.yield(); } } public static void main(String[] args) { TestYield task = new TestYield(); new Thread(task).start(); new Thread(task).start(); }} 线程可以通过调度器(Scheduler)来实现合并或者插队。在Java中,可以使用ExecutorService来管理线程池。
并行访问共享资源时,如果没有采取同步措施,可能会导致数据竞争和纷乱。如下面的例子使用了一个非线程安全的集合:
public class UnsafeList { public static void main(String[] args) { ArrayList list = new ArrayList(); for (int i = 0; i < 1000; i++) { new Thread(() -> { list.add(Thread.currentThread().getName()); }).start(); } System.out.println(list.size()); }} 通过使用synchronized关键字可以实现方法同步。以下是一个线程安全的集合操作示例:
public class SafeList { public static void main(String[] args) { ArrayList list = new ArrayList(); for (int i = 0; i < 1000; i++) { new Thread(() -> { synchronized (list) { list.add(Thread.currentThread().getName()); } }).start(); } try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); }} 代码块中的synchronized可以实现对特定对象的锁定。以下是一个线程安全的集合操作示例:
public class SafeList { public static void main(String[] args) { ArrayList list = new ArrayList(); for (int i = 0; i < 1000; i++) { new Thread(() -> { synchronized (list) { list.add(Thread.currentThread().getName()); } }).start(); } try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); }} 死锁是多个线程同时阻塞,无法继续执行的状态。避免死锁的关键是及时释放锁。以下是一个可能导致死锁的示例:
public class DeadLock { // 假设选择和lipstick和mirror private int choice = 100; private String lipstick; private String mirror; public void make() throws InterruptedException { if (choice == 0) { synchronized (lipstick) { System.out.println(this.girlName + "获得了口红"); Thread.sleep(1000); synchronized (mirror) { System.out.println(this.girlName + "获得了镜子"); } } } else { synchronized (mirror) { System.out.println(this.girlName + "获得了镜子"); Thread.sleep(1000); synchronized (lipstick) { System.out.println(this.girlName + "获得了口红"); } } } }} Lock是一种显式的锁机制,可以提供更细粒度的同步控制。以下是一个Lock的示例:
public class TestLock implements Runnable { private final ReentrantLock lock = new ReentrantLock(); private Integer ticketNum = 10; public void run() { while (true) { if (ticketNum <= 0) { break; } lock.lock(); try { System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNum-- + "张票"); Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } public static void main(String[] args) { TestLock ticket = new TestLock(); new Thread(ticket, "小明").start(); new Thread(ticket, "小红").start(); new Thread(ticket, "黄牛党").start(); }} 生产者消费者问题是一个经典的线程协作问题。以下是一个使用管程法的解决方案:
public class SynContainer { Chicken[] chickens = new Chicken[10]; private int count = 0; public synchronized void push(Chicken chicken) { if (count == chickens.length) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } chickens[count] = chicken; count++; this.notifyAll(); } public synchronized Chicken pop() { if (count == 0) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } count--; this.notifyAll(); return chickens[count]; }} 线程通信是线程协作的重要部分。以下是一个线程通信的示例:
public class CommExample { private Integer result = 0; private boolean flag = false; public void produce() { if (!flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } result = 101; this.notifyAll(); this.flag = !this.flag; } public void consume() { if (flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("Consumer consumed: " + result); this.notifyAll(); this.flag = !this.flag; } public static void main(String[] args) { CommExample ce = new CommExample(); Runnable producer = () -> ce.produce(); Runnable consumer = () -> ce.consume(); new Thread(producer).start(); new Thread(consumer).start(); }} 线程池可以有效管理线程资源,提供更高效的资源利用率和更好的线程管理。
public class TestPool { public static void main(String[] args) { ExecutorService service = Executors.newFixedThreadPool(10); service.execute(new MyTask()); service.execute(new MyTask()); service.shutdown(); } public static class MyTask implements Runnable { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "正在运行..."); } } }} 通过线程池可以简化线程管理代码,提高线程复用率,避免资源浪费。
转载地址:http://tiwaz.baihongyu.com/