程序(program):是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。
进程(process):是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程——生命周期。
如:运行中的QQ、运行中的播放器。
程序是静态的,进程是动态的。
进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域。
线程(thread):进程可进一步细化为线程,是一个程序内部的一条执行路径。
若一个进程同一时间并行执行多个线程,就是支持多线程。
线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(PC),线程切换的开销小。
一个进程中的多个线程共享相同的内存单元/内存地址空间->它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患。
继承Thread类
重写Thread类的run()方法 ——>将此线程执行的操作声明在run()方法中
创建Tread类的子类对象
通过此对象调佣start()方法
例子:计算100以内的所有的偶数
class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(i);
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start(); //start()作用:①启动当前线程 ②调佣当前线程的run()方法
}
}
// 使用多线程计算100以内的质数
class MyThread extends Thread{
@Override
public void run() {
boolean flag = true;
for (int i = 2; i <= 100; i++) {
for (int j = 2; j < i; j++) { // 除以这个数前面的数
if (i % j == 0){ //如果能被整除的话,则这个数不是质数
flag = false;
}
}
if (flag){
System.out.println(i);
}
flag = true;
}
}
}
public class ThreadTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start(); //start()作用:①启动当前线程 ②调佣当前线程的run()方法
}
}
创建一个实现了Runnable接口的类
实现类去实现Runnable中的抽象方法:run()
创建实现类的对象
将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
通过Thread类的对象调佣start()
例子:计算100以内所有的偶数
class MyThread1 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(i);
}
}
}
}
public class ThreadTest2 {
public static void main(String[] args) {
MyThread1 myThread1 = new MyThread1();
Thread thread = new Thread(myThread1);
thread.start();
}
}
例子:创建三个窗口卖票,总票数为100张。实现Runnable接口的方式。
问题:1.卖票的过程中出现了 重票、错票 -------> 线程的安全问题。
产生问题的原因:当某个线程操作车票的过程中,在没有完成的情况下,又有其他线程也参与进来。
如何解决:当一个线程正在操作共享数据的时候,其他线程不能参与出来,直到当前线程操作完成之后,其他线程才可以开始操作。
这种情况即使出现了阻塞,也不能被改变。
在java开发过程中:通过同步机制,来解决线程的安全问题。
synchronized(同步监视器){
? //需要被同步的代码 说明:操作共享数据的代码,即为需要被同步的代码。
? //同步监视器,俗称:锁;任何一个类的对象都可以充当锁。
? //要求:多个线程必须要公用同一把锁。
}
class Window1 implements Runnable{
private int ticket = 100;
Object obj = new Object();
@Override
public void run() {
while (true){
synchronized (obj){
if (ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + "卖票,票号为:" + ticket);
ticket--;
}else{
break;
}
}
}
}
}
public class WindowTest1 {
public static void main(String[] args) {
Window1 window1 = new Window1();
Thread thread1 = new Thread(window1);
Thread thread2 = new Thread(window1);
Thread thread3 = new Thread(window1);
thread1.setName("窗口1");
thread2.setName("窗口2");
thread3.setName("窗口3");
thread1.start();
thread2.start();
thread3.start();
}
}
如果操作共享数据的代码完整的声明在一个方法中,我们可以把这个方法声明为同步的。
class Window2 implements Runnable {
private int ticket = 100;
@Override
public void run() {
while (true) {
show();
}
}
private synchronized void show() {
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + "卖票,票号为:" + ticket);
ticket--;
}
}
}
public class WindowTest2 {
public static void main(String[] args) {
Window2 window2 = new Window2();
Thread thread1 = new Thread(window2);
Thread thread2 = new Thread(window2);
Thread thread3 = new Thread(window2);
thread1.setName("窗口1");
thread2.setName("窗口2");
thread3.setName("窗口3");
thread1.start();
thread2.start();
thread3.start();
}
}
class PrintNum implements Runnable{
private int number = 1;
@Override
public void run() {
while (true){
//同步代码块,解决线程安全问题,this表示当前的对象,一定要唯一。
synchronized (this){
notify();//唤醒单个阻塞的线程
// notifyAll();//唤醒全部阻塞的线程
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (number <= 100){
System.out.println(Thread.currentThread().getName() + ":" + number);
number++;
//实现交替打印,当一个线程执行之后,让它进入阻塞状态。
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
break;
}
}
}
}
}
public class CommunicationTest {
public static void main(String[] args) {
PrintNum printNum = new PrintNum();
Thread thread1 = new Thread(printNum);
Thread thread2 = new Thread(printNum);
thread1.setName("线程1");
thread2.setName("线程2");
thread1.start();
thread2.start();
}
}
相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
不同点:Thread类中声明sleep()方法,Object类中声明wait()方法。
? sleep()方法可以在任何需要的场景下调用,wait()方法必须在同步代码块或同步方法中调用。
? 如果是两个方法都是用在同步代码块或同步方法中,sleep()方法不会释放同步监视器,wait()方法会释放同步监视器。
重写call()方法,可以有返回值。
方法可以抛出异常。
支持泛型的返回值。
需要借助FutureTask类,比如获取返回结果。
Future接口:
可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。
FutrueTask是Futrue接口的唯一的实现类。
FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
提前创建多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池。
Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池。
Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池。
Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池。
Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
原文:https://www.cnblogs.com/whoops-zq/p/15060743.html