Java 基础知识点 笔记总结
Posted IT_Holmes
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 基础知识点 笔记总结 相关的知识,希望对你有一定的参考价值。
文章目录
- 1. Intellij IDEA 软件使用
- 2. 多线程 的概念
- 3. 方式一 继承Thread 线程
- 4. 方式二 实现Runnable接口 创建多线程
- 5. 两种创建多线程方式的 对比
- 6. JVM 和 进程线程关系
- 7. 线程的 生命周期
- 8. 线程的 安全问题
- 9. 使用同步代码块解决 实现Runnable接口的 线程安全问题
- 10. 使用同步代码块解决 继承Thread的 线程安全问题
- 11. 使用同步方法解决 实现Runnable接口的 线程安全问题
- 12. 使用同步方法解决 继承Thread类的 线程安全问题
- 13. 死锁 问题
- 14. Lock锁的方式解决 多线程安全问题
- 15. 线程的通信
- 16. 创建线程方式三 :实现Callable接口
- 17. 方式四:使用线程池创建(开发常用)
1. Intellij IDEA 软件使用
和eclipse不同的简写方式:
- psvm = main方法。
- sout = System.out.println();
public class TestStart {
//psvm等于下面的main方法
public static void main(String[] args) {
//"hello,world".sout 就等于下面的内容
System.out.println("hello , world");
}
}
eclipse 和 IDEA的区别:
IDEA软件的对于一些快捷键,查看setting =》 Keymap查看相应的快捷键就可以了。
IDEA软件对于快速填充的标识符修改查看等等:
2. 多线程 的概念
程序,进程,线程的概念:
>传统进程 和 多线程进程:
- 方法区:包含静态方法
- 堆:包含各种不同的对象。
- 方法区和堆是一个进程一份,换句话说,该进程中的线程都要共享一个方法区和堆。
- 虚拟机栈和程序计数器,每一个线程都会有一个虚拟机栈和程序计数器。每个线程一份。
单核CPU 和 多核CPU , 并行 和 并发 的概念!!!
3. 方式一 继承Thread 线程
3.1 继承Thread 创建线程
Java语言通过java.lang.Thread类来实现多线程的!
上图对应下面代码:
(注意:run()方法是重写Thread类后的run()方法,将此线程执行的操作声明在run()方法中)
Thread创建线程格式如下:
public class TestStart{
/*
多线程创建:方式一继承于Thread类
1. 创建一个继承于Thread类的子类
2. 重写Thread类的run()方法 --> 将此线程执行的操作声明在run()方法中
3. 创建Thread类的子类的对象
4. 通过此对象调用start()方法
*/
public static void main(String[] args) {
//new MyThread(); alt + enter 可以自动补全声明内容
MyThread my = new MyThread();
//start()方法作用就是启动当前线程,调用当前线程的run()方法。
my.start();
//如下操作仍然是在main线程中执行的
//调用玩start以后,该my线程已经开始执行,但是主线程依然向下执行!!!
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.println("主线程的i:" + i);
}
}
//这样就相当于my子线程执行的同时,也不影响主线程的执行。
}
}
class MyThread extends Thread{
@Override
public void run(){
for (int i = 0;i<100;i++){
if(i % 2 ==0){
System.out.println(i);
}
}
}
}
3.2 Thread方式 两个问题
问题一:我们不能通过直接调用run()方法启动线程。
如果直接调用run()方法,也仅仅是调用了该run()方法。并没有开启线程,自始至终都是main线程而已。
我们通过使用Thread.currentThread().getName()来获取当前线程的名字。
public class TestStart{
/*
多线程创建:方式一继承于Thread类
1. 创建一个继承于Thread类的子类
2. 重写Thread类的run()方法 --> 将此线程执行的操作声明在run()方法中
3. 创建Thread类的子类的对象
4. 通过此对象调用start()方法
*/
public static void main(String[] args) {
//new MyThread(); alt + enter 可以自动补全声明内容
MyThread my = new MyThread();
my.start();
//调用玩start以后,该my线程已经开始执行,但是主线程依然向下执行!!!
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
//这样就相当于my子线程执行的同时,也不影响主线程的执行。
}
}
class MyThread extends Thread{
@Override
public void run(){
for (int i = 0;i<100;i++){
if(i % 2 ==0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
问题二:再启动一个相同的线程,会怎么样?
会抛出异常!
其实注释中就有介绍:Throws:
IllegalThreadStateException – if the thread was already started.
因此,我们不能同时调用相同的线程,而是定义不同的对象调用不同的线程。
3.3 Thread匿名对象创建线程
匿名对象调用线程:
package com.holmes.java;
public class ThreadDemo {
public static void main(String[] args) {
//匿名对象也可以用来调用多线程
new Thread(){
@Override
public void run(){
for (int i=0;i<100;i++){
if (i % 2 == 0)
System.out.println(Thread.currentThread().getName());
}
}
}.start();
}
}
3.4 Thread类常用的方法
Thread类常用的方法:
设置线程名称两种方式:
- 自己setName()方法设置线程名字。
- 通过继承父类构造器方法来设置线程名字。
注意的是:因为像sleep,join等等方法都是抛出异常的,因此我们要设置throws或try-catch异常!
此外,再说一点异常范围问题,子类的异常范围一定小于父类的范围异常!!
package com.holmes.java;
/*
1. start()方法:启动当前线程;调用当前线程的run()方法。
2. run()方法:重写此方法,将创建的线程要执行的操作声明在此方法中。
3. currentThread()方法:静态方法,返回执行当前代码的线程,就是当前创建的线程对象。
4. getName()方法:获取当前线程的名字。
5. setName()方法: 设置当前线程的名字。
6. yield()方法: 释放当前CPU的执行权,当然有可能下一刻又分配到当前的线程。
7. join()方法: 在线程a中调用线程b的join()方法,此时的线程a就进入阻塞状态,直到线程b完全执行完以后线程a才结束阻塞状态,
再往后就看系统怎么分配资源了。
8. stop()方法:已过时,当执行此方法时,强制结束当前线程。
9. sleep(long millitime)方法:让当前线程“睡眠,也就是阻塞”指定的millitime毫秒(在睡眠的时间,线程是阻塞状态)。
10. isAlive()方法: 判断当前线程是否存活。
*/
public class ThreadMethodTest {
public static void main(String[] args) {
//第一种:通过构造器传递String参数来命名,这在Thread源码中是具备的!
ThreadMethods tm = new ThreadMethods("线程一");
//第二种:可以自己给tm线程重新命名
//tm.setName("线程一");
tm.start();
//怎样给主线程命名呢?
//直接通过Thread.currentThread().setName("XXX")设置就可以了
Thread.currentThread().setName("主线程");
for (int i=0;i<100;i++){
if (i % 2 == 0)
System.out.println(Thread.currentThread().getName() + ":" +i);
if (i == 20){
try {
//join()方法使当前线程进入阻塞状态,等待另一个线程调用结束,再进行。
tm.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//判断tm线程是否还存在
System.out.println(tm.isAlive());
}
}
class ThreadMethods extends Thread{
@Override
public void run(){
for (int i=0;i<100;i++){
if (i % 2 == 0){
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" +i);
}
//if(i % 20 == 0){
//释放当前CPU的执行权,当然有可能下一刻又分配到当前的线程
//yield();
//}
}
}
public ThreadMethods(String name){
//通过继承Thread的构造器方法,来命名线程名字
super(name);
}
}
3.5 线程的调度
线程优先级:
- MAX_PRIORITY :10
- MIN_PRIORITY :1
- NORM_PRIORITY :5
如何获取和设置当前线程的优先级:
- getPriority() :获取线程的优先级。
- setPriority(int p) :设置线程的优先级。
注意:高优先级的线程要抢占低优先级线程cpu执行权,但是只是从概率上来讲,并不是从一开始只有当高优先级线程执行完以后再执行低优先级。
package com.holmes.java;
public class ThreadMethodTest {
public static void main(String[] args) {
ThreadMethods tm = new ThreadMethods("线程一");
//设置线程优先级
tm.setPriority(Thread.MAX_PRIORITY);
tm.start();
//怎样给主线程命名呢?
//直接通过Thread.currentThread().setName("XXX")设置就可以了
Thread.currentThread().setName("主线程");
for (int i=0;i<100;i++){
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority() +"--"+ i);
}
if (i == 20){
try {
//join()方法使当前线程进入阻塞状态,等待另一个线程调用结束,再进行。
tm.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//判断tm线程是否还存在
System.out.println(tm.isAlive());
}
}
class ThreadMethods extends Thread{
@Override
public void run(){
for (int i=0;i<100;i++){
if (i % 2 == 0){
//getPriority()获取线程优先级,提示这里不写TThread.currentThread(),默认就是当前的this,还是该线程。
System.out.println(Thread.currentThread().getName() + ":" + getPriority() + "--"+i);
}
}
}
public ThreadMethods(String name){
//通过继承Thread的构造器方法,来命名线程名字
super(name);
}
}
4. 方式二 实现Runnable接口 创建多线程
package com.holmes.java;
/*
创建多线程方式二:实现Runnable接口
1. 创建一个实现了Runnable接口的类。
2. 实现类去实现Runnable中的抽象方法:run()方法。
3. 创建实现类的对象。
4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象。
5. 通过Thread类的对象调用start()方法。
*/
public class ThreadTest2 {
public static void main(String[] args) {
MyThead3 mt3 = new MyThead3();
//将实现类对象作为参数传递给Thread构造器
//整体上而言就是一个多态的效果
Thread th1 = new Thread(mt3);
//此时这个线程是th1,而不是mt3!
//而这里的start()方法:开启线程调用run()方法,这里的run()方法自然也是Thread中的run()方法。
//之所以还是执行的MyThread3得看源码,如下面图:
th1.setName("线程一");
th1.start();
Thread th2 = new Thread(mt3);
th2.setName("线程二");
th2.start();
}
}
class MyThead3 implements Runnable{
@Override
public void run() {
for (int i=0;i<100;i++){
if (i%2==0){
System.out.println(Thread.currentThread().getName() +":"+ i);
}
}
}
}
start()方法:开启线程调用run()方法,这里的run()方法自然也是Thread中的run()方法。
问题来了,既然是Thread中得run()方法,为什么最后还是调用得实现类MyThread中的run()方法呢?看源码。
5. 两种创建多线程方式的 对比
- 首先,一个继承,一个实现接口。
为什么开发中,优先选择:实现Runable接口的方式?
- 实现的方式没有类的单继承性的局限性。
- 实现的方式更适合来处理多个线程有共享数据的情况。
相同点:两种方式都需要重写run()方法,将线程要执行的逻辑声明在run()中。
通过看Thread源码,差不多也都知道,它们的联系。
6. JVM 和 进程线程关系
线程分为两种:一种守护线程,一种用户线程。
通过调用thread.setDaemon(true),可以将用户线程变成一个守护线程:
7. 线程的 生命周期
Thread.State类定义了线程的几种状态:
新建,就绪,运行,阻塞,死亡
生命周期流程如下:
8. 线程的 安全问题
出现线程安全的关键所在,就是多线程是否存在共享数据。
线程的安全问题:
package com.holmes.java02;
public class WindowTest2 {
public static void main(String[] args) {
MyThread4 m = new MyThread4();
Thread t1 = new Thread(m);
Thread t2 = new Thread(m);
Thread t3 = new Thread(m);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}
class MyThread4 implements Runnable{
private static int ticket = 100;
@Override
public void run() {
while (true){
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread()以上是关于Java 基础知识点 笔记总结 的主要内容,如果未能解决你的问题,请参考以下文章