程式是靜態的,程序是動態的;
程序作為資源分配單位,系統在執行時會為每個程序分配不同的記憶體區域;
執行緒作為排程和執行的單位,每個執行緒擁有獨立的執行棧和程式計數器;
一個程序中的多個執行緒共用相同的記憶體單元/記憶體地址空間–它們從同一堆中分配物件,可以存取相同的變數和物件,這就是執行緒間的通訊更加的簡便、高效,但是多執行緒操作共用的系統資源可能帶來安全隱患;
面試題:建立多執行緒的方式有幾種?
答:四種;
1). 繼承Thread類的方式建立執行緒;
2). 實現Runnable介面建立執行緒;
3). 使用Callable和Future建立執行緒;
4). 使用執行緒池例如用Executor框架;
1.繼承Thread類的方式建立執行緒:
public class Main01 {
public static void main(String[] args) {
//給主執行緒命名:Thread.currentThread()是獲取當前執行緒
Thread.currentThread().setName("主執行緒");
//列印
System.out.println(Thread.currentThread().getName()+"執行緒執行了");
//建立一個執行緒
MyThread myThread = new MyThread();
//啟動執行緒
myThread.start();
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"執行緒執行了");
}
}
//輸出
主執行緒執行緒執行了
Thread-0執行緒執行了
2.實現Runnable介面的方式建立執行緒:
public class Main02 {
public static void main(String[] args) {
//給main執行緒命名
Thread.currentThread().setName("主執行緒");
//列印
System.out.println(Thread.currentThread().getName()+"執行了");
//建立執行緒
MyRunnable myRunnable = new MyRunnable();
//將myRunnable傳入Thread;類中
Thread thread = new Thread(myRunnable);
thread.start();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"執行了");
}
}
//輸出
主執行緒執行緒執行了
Thread-0執行緒執行了
3.使用Callable和FutureTask建立執行緒
public class Main03 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//建立實現類
MyCallable myCallable = new MyCallable();
//建立FutureTask類,該類實現了Runnable介面;
FutureTask stringFutureTask = new FutureTask(myCallable);
//將建立的FutureTask的範例傳遞給Thread類,並啟動執行緒
new Thread(stringFutureTask).start();
Object o = stringFutureTask.get();
System.out.println(o);
}
}
//MyCallable介面實現Callable介面,其中的call方法相當於我們的run方法
class MyCallable implements Callable{
@Override
public Object call() throws Exception {
int h = 0;
for(int i = 1; i <100; i++){
if(i % 2 == 0){
h+=i;
}
}
return Thread.currentThread().getName()+"求和為:--"+h;
}
}
4.使用執行緒池
public class Main04 {
public static void main(String[] args) {
//1.提供指定執行緒數量的執行緒池;
ExecutorService executorService = Executors.newFixedThreadPool(10);
//2.執行指定的執行緒操作,需要提供實現Runnable介面和Callable介面的實現類物件;
executorService.execute(new MyRunnable1());
//關閉連線池;
executorService.shutdown();
}
}
class MyRunnable1 implements Runnable {
@Override
public void run() {
System.out.println("這是使用執行緒池建立執行緒");
}
}
使用執行緒池的好處:
1.提高響應速度(減少了執行緒的建立時間)
2.降低了資源消耗(重複利用執行緒池中的執行緒,不需要每次都建立)
3.便於管理:
corePoolSize:核心池的大小
maximumPoolSize:最大執行緒數;
keepAliveTime:執行緒沒有任務是最多保持多長時間後終止
1.start():啟動當前執行緒;呼叫當前執行緒的run();
2.run(): 通常需要重寫Thread類中的此方法,將要執行的操作寫入該方法中;
3.currentThread(): 靜態方法,返回執行當前程式碼的執行緒;
4.getName():獲取當前執行緒的名字;
5.setName():設定當前執行緒的名字;
6.yield():釋放當前cpu的執行權;
7.join():線上程A中呼叫執行緒B的join(),此時執行緒A就會進入阻塞狀態,直到執行緒B完全執行完以後,執行緒A才結束阻塞狀態;
8.stop():已過時,當執行此方法是,強制結束當前執行緒;
9.sleep(long millitime) : 使當前執行緒睡眠指定的毫秒數,在此期間,該執行緒處於阻塞狀態;
10. isAlive(): 判斷當前執行緒是否存活;
1.多個執行緒執行的不確定性造成執行結果的不穩定
2.多個執行緒對賬本的共用,會造成操作的不完整性,會破壞資料;
解決執行緒安全的方式有幾種?
答:3種;
1.同步程式碼塊—synchronized(同步監視器){需要被同步的程式碼}
public class SYNdemo01 {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread1 = new Thread(myRunnable);
Thread thread2 = new Thread(myRunnable);
thread1.start();
thread2.start();
}
}
class MyRunnable implements Runnable{
private int x = 150;
@Override
public void run() {
while (true){
synchronized (this){
if(x>0){
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"視窗賣出了:"+x+"號票");
x--;
}else{
break;
}
}
}
}
}
synchronized(鎖){需要被同步的程式碼}
1.操作共用資料的程式碼,即需要被同步的程式碼;
2.共用資料,多個執行緒同時操作的變數,比如我們現實中的火車票
3.同步監視器:俗稱:鎖,任何一個類的物件,都可以充當鎖物件;
要求:多個執行緒必須要共用一把鎖,即這個鎖物件在多執行緒操作時必須是同一個鎖物件;
2.同步方法—被synchronized修飾的方法
public class SYNdemo01 {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread1 = new Thread(myRunnable);
Thread thread2 = new Thread(myRunnable);
thread1.start();
thread2.start();
}
}
class MyRunnable implements Runnable{
private int x = 150;
@Override
public void run() {
while (true){
loket();
if(x<=0){
break;
}
}
}
//同步方法
public synchronized void loket(){
if(x>0){
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"視窗賣出了:"+x+"號票");
x--;
}
}
}
3.Lock鎖解決執行緒安全問題
Lock鎖是JDK5.0後加入的,是一個介面,比較常用的是ReentrantLock類,他擁有與synchronized相同的並行性和記憶體語意,在實現執行緒安全的控制中,比較常用的是ReentrantLock,可以顯式加鎖、釋放鎖;
public class LockSuo {
public static void main(String[] args) {
Window window = new Window();
Thread thread1 = new Thread(window);
Thread thread2 = new Thread(window);
thread1.start();
thread2.start();
}
}
class Window implements Runnable{
private static int x = 150;
private ReentrantLock reentrantLock = new ReentrantLock();
@Override
public void run() {
while (true){
try{
reentrantLock.lock();
if(x>0){
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"視窗出售了:"+x+"號票");
x--;
}else{
break;
}
}finally {
reentrantLock.unlock();
}
}
}
}
class BankTest{
private static BankTest bankTest;
private BankTest(){
}
public static BankTest getBankTest(){
synchronized (BankTest.class) {
if (bankTest == null) {
bankTest = new BankTest();
}
}
return bankTest;
}
}
死鎖: 不同的執行緒分別佔用對方需要的同步資源不放棄,都在等待對方放棄自己需要的同步資源,就形成了執行緒的死鎖;
死鎖出現後,不會出現異常,不會出現提示,只是所有的執行緒都處於阻塞狀態,無法繼續;
死鎖演示:
public class SYNdemo02 {
public static void main(String[] args) {
StringBuffer s1 = new StringBuffer();
StringBuffer s2 = new StringBuffer();
new Thread(){
@Override
public void run() {
synchronized (s1){
s1.append("a");
s2.append("1");
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (s2){
s1.append("b");
s2.append("2");
System.out.println(s1);
System.out.println(s2);
}
}
}
}.start();
new Thread(){
@Override
public void run() {
synchronized (s2){
s1.append("c");
s2.append("3");
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (s1){
s1.append("d");
s2.append("4");
System.out.println(s1);
System.out.println(s2);
}
}
}
}.start();
}
}
例題:交替列印1-100;執行緒1和執行緒2交替列印;
public class JT {
public static void main(String[] args) {
Number number = new Number();
Thread thread1 = new Thread(number);
Thread thread2 = new Thread(number);
thread1.start();
thread2.start();
}
}
class Number implements Runnable {
private static int x = 1;
@Override
public void run() {
while (true) {
synchronized (this) {
if (x <= 100) {
notify();
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "列印了:" + x);
x++;
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}
}
}
}