首页
友链
Search
1
JAVA IO
176 阅读
2
SpringBoot整合SpringCache
138 阅读
3
微服务项目搭建
97 阅读
4
Docker搭建Typecho博客
95 阅读
5
wlop 4K 壁纸 4k8k 动态 壁纸
82 阅读
解决方案
JAVA基础
JVM
多线程
开源框架
数据库
前端
分布式
框架整合
中间件
容器部署
设计模式
数据结构与算法
开发工具
百度网盘资源
天翼网盘资源
阿里网盘资源
登录
Search
标签搜索
java
javase
springboot
docker
thread
spring
分布式
锁
redis
linux
typecho
源码
mysql
software
git
算法
synchronized
ArrayList
springboot整合
ThreadPool
少年
累计撰写
130
篇文章
累计收到
5
条评论
首页
栏目
解决方案
JAVA基础
JVM
多线程
开源框架
数据库
前端
分布式
框架整合
中间件
容器部署
设计模式
数据结构与算法
开发工具
百度网盘资源
天翼网盘资源
阿里网盘资源
页面
友链
搜索到
26
篇与
java
的结果
2022-02-28
DBUtils进行数据库CRUD
通过读取properties获取链接数据库信息,利用apache开源数据库操作工具包DBUtils进行数据库CRUD操作。properties文件:driverClass=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/test user=root password=11111通过dbutils自定义获取数据库连接对象:package learn.javase.jdbc; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.util.Properties; public class JdbcUtils{ private static Connection conn; private static String driverClass; private static String url; private static String user; private static String password; static { try { getReadConfig(); Class.forName(driverClass); conn = DriverManager.getConnection(url, user, password); }catch(Exception e) { new RuntimeException("链接数据库失败!"); } } private static void getReadConfig() throws Exception{ InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties"); Properties pro = new Properties(); pro.load(in); driverClass =pro.getProperty("driverClass"); url = pro.getProperty("url"); user = pro.getProperty("user"); password = pro.getProperty("password"); } public static Connection getConnection() { return conn; } } 使用工具包中的QueryRunner进行数据库的CRUD操作:package learn.javase.jdbc; import java.sql.Connection; import java.sql.SQLException; import org.apache.commons.dbutils.DbUtils; import org.apache.commons.dbutils.QueryRunner; /** * 利用apatche的DBUtils工具包,添加\修改\删除数据 * @author Jole * */ public class DbUtilsDemo { private static Connection conn = JdbcUtils.getConnection(); private static QueryRunner qr = new QueryRunner(); public static void main(String[] args) throws Exception{ // insert(); // update(); delte(); } public static void insert() throws SQLException{ String sql = "insert into my_user(u_id,u_name,u_age) values(11111,'inster',10)"; Object[] param = {}; int n = qr.update(conn, sql, param); System.out.println(n); DbUtils.close(conn); } public static void update() throws SQLException{ String sql ="update my_user set u_name=? ,u_age=? where u_id=?"; Object[] param = {"ceshi",1000,11111}; int n = qr.update(conn, sql, param); DbUtils.close(conn); System.out.println(n); } public static void delte() throws SQLException{ String sql = "delete from my_user where u_id=?"; int n = qr.update(conn, sql, 11111); DbUtils.close(conn); System.out.println(n); } } 封装QueryRunner返回结果成对象:要封装成的对象Userpackage learn.javase.jdbc; public class User { private String u_id; private String u_name; private String u_age; public String getU_id() { return u_id; } public void setU_id(String u_id) { this.u_id = u_id; } public String getU_name() { return u_name; } public void setU_name(String u_name) { this.u_name = u_name; } public String getU_age() { return u_age; } public void setU_age(String u_age) { this.u_age = u_age; } public User() { super(); // TODO Auto-generated constructor stub } public User(String u_id, String u_name, String u_age) { super(); this.u_id = u_id; this.u_name = u_name; this.u_age = u_age; } @Override public String toString() { return "User [u_id=" + u_id + ", u_name=" + u_name + ", u_age=" + u_age + "]"; } } 根据返回结果,通过实现ResultHandler接口的实现类,封装对象:package learn.javase.jdbc; import java.sql.Connection; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.ArrayHandler; import org.apache.commons.dbutils.handlers.ArrayListHandler; import org.apache.commons.dbutils.handlers.BeanHandler; import org.apache.commons.dbutils.handlers.BeanListHandler; import org.apache.commons.dbutils.handlers.ColumnListHandler; import org.apache.commons.dbutils.handlers.MapHandler; import org.apache.commons.dbutils.handlers.MapListHandler; import org.apache.commons.dbutils.handlers.ScalarHandler; /** * 利用开源apache的commons-dbutils-1.7.jar的QueryRunner封装数据 * @author Jole * */ public class QueryRunnerDemo01 { public static final Connection conn = JdbcUtils.getConnection(); public static final QueryRunner qr = new QueryRunner(); public static void main(String[] args) throws Exception { // TODO Auto-generated method stub // getInfo(); // getInfo2(); // getInfo3(); // getInfo4(); // getInfo5(); // getInfo6(); // getInfo7(); getInfo8(); } //ArrayHandler 第一行 public static void getInfo() throws Exception{ String sql ="select * from my_user"; Object[] rs = qr.query(conn, sql, new ArrayHandler()); for(Object obj : rs) { System.out.print(obj + "\t"); } } //ArrayListHandler 所有记录 public static void getInfo2() throws Exception{ String sql ="select * from my_user"; List<Object[]> rs = qr.query(conn, sql, new ArrayListHandler()); for(Object[] objs : rs) { for(Object obj : objs) { System.out.print(obj + "\t"); } System.out.println(); } } //javaBean 第一行封装成对象 public static void getInfo3() throws Exception{ String sql ="select * from my_user"; User user = qr.query(conn, sql, new BeanHandler<User>(User.class)); System.out.println(user); } //javaBean 所有记录封装成对象 public static void getInfo4() throws Exception{ String sql ="select * from my_user"; List<User> userList = qr.query(conn, sql, new BeanListHandler<User>(User.class)); for(User user : userList) { System.out.println(user); } } //ColumnListHandler所有记录的某列值 public static void getInfo5() throws Exception{ String sql ="select * from my_user"; List<Object> list = (List<Object>) qr.query(conn, sql, new ColumnListHandler("u_name")); for(Object obj : list) { System.out.println(obj); } } //ScalarHandler返回单个值 public static void getInfo6() throws Exception{ String sql ="select count(*) from my_user"; Long count = qr.query(conn, sql, new ScalarHandler<Long>()); System.out.println(count); } //MapHandler将第一行数据封到map中 public static void getInfo7() throws Exception{ String sql ="select * from my_user"; Map<String, Object> map = qr.query(conn, sql, new MapHandler()); Set<String> set =map.keySet(); for(String key : set) { System.out.println(key+" "+map.get(key)); } } //MapListHandler将所有数据封到list中的map中 public static void getInfo8() throws Exception{ String sql ="select * from my_user"; List<Map<String, Object>> list= qr.query(conn, sql, new MapListHandler()); for(Map<String, Object> map : list) { for(String key : map.keySet()) { System.out.print(key+" "+ map.get(key)); } System.out.println(); } } } 主要涉及的jar包:commons-dbutils-1.7.jar(基本的CRUD)、mysql-connector-java-5.1.37-bin.jar(数据库连接驱动)
2022年02月28日
42 阅读
0 评论
1 点赞
2022-02-28
DBCP连接池
使用dbcp连接池,进行数据库的CRUD,涉及jar包:commons-dbcp2-2.8.0.jar(连接池包)、commons-pool2-2.9.0.jar(dbcp依赖该包)、commons-logging-1.2.jar(接口日志包)、mysql-connector-java-5.1.37-bin.jar(数据库连接驱动)、commons-dbutils-1.7.jar(基本CRUD操作)DbcpUtils数据库连接池配置:package learn.javase.dbcppool; import org.apache.commons.dbcp2.BasicDataSource; /** * 利用DBCP链接数据库 * @author Jole * */ public class DbcpUtils { private static BasicDataSource dataSource = new BasicDataSource(); static { //必配参数 dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/test"); dataSource.setUsername("root"); dataSource.setPassword("11111"); //可配参数 dataSource.setInitialSize(10); dataSource.setMaxIdle(8); dataSource.setMinIdle(2); } public static BasicDataSource getConnecton() { return dataSource; } } 利用DbcpUtils进行数据库的CRUD操作:package learn.javase.dbcppool; import java.sql.SQLException; import java.util.List; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.ArrayListHandler; /** * 利用DBCP数据库连接池增加、查询 * @author Jole * */ public class DbcpUtilsDemo { private static QueryRunner qr = new QueryRunner(DbcpUtils.getConnecton()); public static void main(String[] args) { // TODO Auto-generated method stub // insertData(); findData(); } public static void insertData() { String sql = "insert into my_user(u_id,u_name,u_age) values(?,?,?)"; Object[] params = {"777777","dbcpUTils",30}; try { int num = qr.update(sql, params); System.out.println(num); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void findData() { String sql = "select * from my_user"; try { List<List<Object[]>> objs = qr.execute(sql, new ArrayListHandler()); for(List<Object[]> obj : objs ) { for(Object ob : obj) { System.out.println(ob); } } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } 除了DBCP数据库连接池外,还有目前比较常用的c3p0、Druid等数据库链接池。
2022年02月28日
20 阅读
0 评论
1 点赞
2022-02-28
JAVA原生JDBC操作
利用JAVA原生JDBC进行数据库的CRUD操作。简单记录一下。package learn.javase.jdbc; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Scanner; import java.sql.PreparedStatement; import java.sql.Connection; /** * jdbc使用 * @author Jole * */ public class MyDriver { public static void main(String[] args) throws ClassNotFoundException, SQLException{ //1.注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2.获取数据库连接对象 String url = "jdbc:mysql://localhost:3306/test"; String user = "root"; String password = "111111"; Connection conn = DriverManager.getConnection(url, user, password); // System.out.println(conn); //3.获取连接平台的执行对象 // Statement st = conn.createStatement(); //4.执行sql,返回行数 //executeUpdate():增删改 //String sql = "insert into user(user_id,user_name,user_password) values('1320442338427273213','zss','12')"; //int n = st.executeUpdate(sql); Scanner scan = new Scanner(System.in); String name = scan.nextLine(); int age = scan.nextInt(); //为了防止sql注入攻击和提高效率,使用Statement的子类,进行预编译 //executeQuery():查询 String sql = "select * from my_user where u_name=? and u_age=?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setObject(1, name); ps.setObject(2, age); System.out.println(sql); ResultSet rs = ps.executeQuery(); //5.处理结果 while(rs.next()) { System.out.println(rs.getString("u_name")+ " " + rs.getString("u_age")); } //6.释放资源 rs.close(); ps.close(); conn.close(); } }
2022年02月28日
39 阅读
0 评论
2 点赞
2022-02-28
JAVA反射
获取反射对象的3种方法。通过反射创建对象、通过反射获取有参、无参、公有、私有构造方法。通过反射获取公有、私有成员变量并修改之。通过反射获取成员方法,并调用。通过反射泛型擦除,存值。通过读取配置文件运行反射方法。package learn.javase.mycalss; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; /** * 一、获取class的三种方式 * * 二、获取公有构造方法 * 注意: * 1、被反射的类必须是有空参数的构造器 * 2、构造方法必须是public的才能反射获取 * * 三、获取所有构造方法 * * @author Jole * */ public class ClassDemo { public static void main(String[] args) throws Exception{ // 方式1、.getClass() Person p = new Person(); Class c = p.getClass(); System.out.println(c); //方式2、类名.class Class c3 = Person.class; System.out.println(c3); //方式3、Class.forName(类全名); Class c2 = Class.forName("learn.javase.mycalss.Person"); System.out.println(c2); Class personClass = Class.forName("learn.javase.mycalss.Person"); //获取public所有构造方法 Constructor[] cons = personClass.getConstructors(); for(Constructor con : cons) { System.out.println(con); } //获取public无参构造方法 Constructor cc = personClass.getConstructor(); Person pp = (Person)cc.newInstance(); //获取public有参构造方法 Constructor ccc = personClass.getConstructor(String.class, int.class); Person ppp = (Person)ccc.newInstance("张三",122); System.out.println(ppp); //获取所有包括private的构造方法 Constructor[] conss = personClass.getDeclaredConstructors(); for(Constructor co: conss ) { System.out.println(co); } //获取私有构造方法 Constructor ct = personClass.getDeclaredConstructor(String.class); ct.setAccessible(true); Person ps = (Person)ct.newInstance("zhangsan"); System.out.println(ps.toString()); //获取所有公共成员变量 Field[] fis = personClass.getFields(); for(Field fi : fis) { System.out.println("成员变量:"+fi); } //获取私有成员变量 Field fd = personClass.getDeclaredField("name"); System.out.println(fd); //修改成员变量的值 Object obj = personClass.newInstance(); fd.set(obj, "哈哈"); fd.setAccessible(true); System.out.println(obj); //获取所有公共成员方法 Method[] md = personClass.getMethods(); for(Method m : md) { System.out.println("所有公共成员方法:"+m); } //获取单个无参成员方方法 Method mt = personClass.getMethod("say"); mt.invoke(personClass.newInstance()); //获取单个有参成员方法,并运行 Method me = personClass.getMethod("say", String.class); Object objd = personClass.newInstance(); Object ob = me.invoke(objd, "Hello World"); System.out.println(ob); //反射泛型的擦除 List<String> list = new ArrayList<String>(); //正常情况只能存String类型,但是通过反射可以存其他类型 Class li = list.getClass(); Method mmm = li.getMethod("add", Object.class); mmm.invoke(list, 1000); mmm.invoke(list, 444); mmm.invoke(list,999L); mmm.invoke(list, 'c'); System.out.println(list); } } 通过读取配置文件,用反射调用成员方法,更加灵活。不同的成员方法如下:人类对象package learn.javase.mycalss; public class People { public void say() { System.out.println("我是人类"); } } 学生对象package learn.javase.mycalss; public class Student { public void stay() { System.out.println("我要学习"); } } 打工人对象package learn.javase.mycalss; public class Worker { public void job() { System.out.println("我要工作"); } } iconfig.properties配置文件如下:#className=learn.javase.mycalss.People #classMethod=say className=learn.javase.mycalss.Student classMethod=stay #className=learn.javase.mycalss.Worker #classMethod=job通过反射,调用不同对象的成员方法,每次只需要修配置文件即可,不需要修改代码。代码如下:package learn.javase.mycalss; import java.io.FileReader; import java.lang.reflect.Method; import java.util.Properties; /** * 读取配置文件,通过反射调用不同对象的成员方法 * @author Jole * */ public class ConfigPropertiesRun { public static void main(String[] args) throws Exception{ //读取配置文件,注意配置文件路径为src下 FileReader fr = new FileReader("iconfig.properties"); Properties pr = new Properties(); pr.load(fr); fr.close(); //获取配置文件数据 String className = pr.getProperty("className"); String classMethod = pr.getProperty("classMethod"); //反射,并获取对象 Class c = Class.forName(className); Object obj = c.newInstance(); //反射获取成员方法 Method m = c.getMethod(classMethod); //反射调用成员方法 m.invoke(obj); } }
2022年02月28日
19 阅读
0 评论
0 点赞
2022-02-28
JAVA多线程
进程:CPU为每个应用程序分配的独立空间,一个进程可能有多个线程。进程为线程中的摸个执行任务程序,多个进程之间可以进行共享数据。而JAVA的线程则是由JVM进程分配的,main方法所在线程,则成为主线程。〇、线程状态正常情况线程执行步骤:新建-》运行-》死亡当CPU资源不够时,CUP分配给各个线程的资源可能不同(貌似有点像是线程在抢资源,实际是CPU分配资源给每个线程)。因此就会出现线程的阻塞、休眠、等待3个状态。其中阻塞状态,当cpu资源够时,阻塞状态的线程可能恢复到运行状态,而休眠、等待的线程也可能进入运行状态。休眠、等待状态可能转换成阻塞状态,但是阻塞状态不会变成休眠、等待状态。一、多线程的实现0、线程常用方法package learn.javase.threads; /** * 继承Thread创建线程,设置线程名称、获取线程名称 * @author Jole * */ public class ThreadDemo01 { public static void main(String[] args) { MyThreadsDemo mt = new MyThreadsDemo(); mt.setName("Hi"); mt.start(); //获取当前线程 Thread t = Thread.currentThread(); System.out.println(t.getName()); } }线程实现方式主测试类:package learn.javase.threads; public class MyThreadsDemo extends Thread{ public MyThreadsDemo() { super("Google"); } public void run() { for(int i=0;i<5;i++) { try { Thread.sleep(1000); System.out.println(i); }catch(Exception e) { System.out.println(e.getMessage()); } } } }线程测试主类:package learn.javase.threads; /** * 实现多线程的4中方式: * 1、继承Thread类 * 2、实现Runnable接口 * 3、匿名内部类 * 4、匿名接口 * 5、实现Callable接口,与Runnable的区别: * 可以有返回值 * 可以抛异常 * @author Jole * */ public class ThreadNumberDemo { public static void main(String[] args) { // TODO Auto-generated method stub //方式1,继承Thread new ThreadThread1().start(); //方式2,实现Runnable接口 new Thread(new ThreadRunnables2()).start(); //方式3,匿名内部类 new Thread() { public void run() { System.out.println("匿名内部类,实现多线程"); } }.start(); //方式4,匿名接口 Runnable r = new Runnable() { public void run() { System.out.println("匿名接口,实现多线程"); } }; new Thread(r).start(); //方式4的简洁版 new Thread(new Runnable() { public void run() { System.out.println("匿名接口简洁版,实现多线程"); } }).start(); } }1、继承Threadpackage learn.javase.threads; /** * 方式一继承Thread实现多线程 * @author Jole * */ public class ThreadThread1 extends Thread{ public void run() { System.out.println("extends ....thread"); } }2、实现Runnable接口package learn.javase.threads; /** * 实现Runnable接口 * @author Jole * */ public class ThreadRunnables2 implements Runnable{ public void run() { System.out.println("runnable .. threads"); } }3、实现Callable通过使用线程池,实现Callable<T>,主要用于有返回值和抛出异常的。如下的线程池实现。4、区别继承Thread接口与实现Runnable接口,实现多线程区别:1、单继承、多实现2、实现Runnable可以将线程与任务(run())解耦package learn.javase.threads; /** * 实现Runnable接口,创建线程。 * 继承Thread接口与实现Runnable接口,实现多线程区别: * 1、单继承、多实现 * 2、将线程与任务(run())解耦 * @author Jole * */ public class RunnableDemo { public static void main(String[] args) { // TODO Auto-generated method stub new Thread(new RunnableThreadDemo()).start(); for(int i=0;i<5;i++) { System.out.println("main..."+i); } } }线程实现类:package learn.javase.threads; public class RunnableThreadDemo implements Runnable{ public void run() { for(int i=0;i<5;i++) { System.out.println("run..."+i); } } }二、线程池1、线程池使用package learn.javase.threads; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 线程池 * @author Jole * */ public class ThreadPoolDemo { public static void main(String[] args) { //使用线程池工场Executors,创建线程池 ExecutorService es = Executors.newFixedThreadPool(2); //使用线程池,构造器为实现Runnable的接口,使用完后自动放回线程池 es.submit(new ThreadPoolThreas()); es.submit(new ThreadPoolThreas()); //如果线程池里面线程不够了,只有等待其它线程执行完后,在使用 es.submit(new ThreadPoolThreas()); //一般不用关闭线程池,特殊情况关闭线程池,可以使用 // es.shutdown(); } }线程池线程实现Runnable接口:package learn.javase.threads; public class ThreadPoolThreas implements Runnable{ public void run() { System.out.println(Thread.currentThread().getName()+"线程池的使用"); } }2、线程池-实现多线程package learn.javase.threads; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * 通过线程池,实现Callable接口,实现多线程,并获取任务(run)返回值,和异常 * @author Jole * */ public class ThreadPoolDemo2 { public static void main(String[] args) throws Exception{ //使用线程池工场Executors创建线程池工场 ExecutorService es = Executors.newFixedThreadPool(2); //通过实现Callable实现线程,提交并返回Future对象 Future t = es.submit(new ThreadCallableDemo()); //通过get获取返回值 System.out.println(t.get()); } }实现类Callable<T>接口类:package learn.javase.threads; import java.util.concurrent.Callable; /** * * @author Jole * 使用继承Callable<T>泛型接口,实现多线程,并使用线程池 */ public class ThreadCallableDemo implements Callable<String>{ public String call() { return "实现Callable接口"; } }3、实例实例1package learn.javase.threads; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * 使用线程池实现异步提交求和:1+...100= 1+... 200= * 注意: * 有返回值 * 计算不同的值 * @author Jole * */ public class ThreadMainDemo1 { public static void main(String[] args) throws Exception{ //使用线程池工场Executors创建线程池 ExecutorService es = Executors.newFixedThreadPool(3); //使用线程池中的线程,并使用构造方法传参,求和并返回 Future<Integer> f =es.submit(new CallableDemo(100)); Future<Integer> f2 =es.submit(new CallableDemo(2100)); System.out.println("1+...100="+f.get()); System.out.println("1+...200="+f2.get()); //关闭线程池 es.shutdown(); } }实现类:package learn.javase.threads; import java.util.concurrent.Callable; /** * 通过实现Callable接口,及构造方法传参,并通过call返回值 * @author Jole * */ public class CallableDemo implements Callable<Integer>{ private int number; //创建构造方法,为了传参 public CallableDemo(int number) { this.number = number; } public Integer call() { int sum = 0; //使用构造方法传的参数求和并返回 for(int i=0;i<=number;i++) { sum=sum+i; } return sum; } }实例2:package learn.javase.threads; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * 通过线程池,实现Callable接口,实现多线程,并获取任务(run)返回值,和异常 * @author Jole * */ public class ThreadPoolDemo2 { public static void main(String[] args) throws Exception{ //使用线程池工场Executors创建线程池工场 ExecutorService es = Executors.newFixedThreadPool(2); //通过实现Callable实现线程,提交并返回Future对象 Future t = es.submit(new ThreadCallableDemo()); //通过get获取返回值 System.out.println(t.get()); } }实现类:package learn.javase.threads; import java.util.concurrent.Callable; /** * * @author Jole * 使用继承Callable<T>泛型接口,实现多线程,并使用线程池 */ public class ThreadCallableDemo implements Callable<String>{ public String call() { return "实现Callable接口"; } }三、线程安全1、线程安全package learn.javase.threads; /** * 可能出现安全问题的情况:多线程,共享一个数据 * 线程安全问题,及解决办法加synchronized,实例:卖票 * @author Jole * synchronized解决线程安全问题,使用公式: * synchronized(锁(任意对象)){ * 代码块 * } * * 注意:因为加了锁,所以效率会降低,但是安全性得到了保证,效率低的原因如下: * 1、每次线程都会先判断是否有锁 * 2、获取锁 * 3、执行完代码块 * 4、执行完后,还会锁 * 5、在等下一个锁执行相同操作,相当于只能排队上一个厕所。 */ public class ThreadSeaerfDemo { public static void main(String[] args) { //多线程出现的安全问题,票有负数还在卖 // Tickets00 t = new Tickets00(); // Thread t0 = new Thread(t); // Thread t1 = new Thread(t); // Thread t2 = new Thread(t); // t0.start();t1.start();t2.start(); //原始代码 // Tickets01 t = new Tickets01(); // Thread t0 = new Thread(t); // Thread t1 = new Thread(t); // Thread t2 = new Thread(t); // t0.start();t1.start();t2.start(); //第一次优化代码:同步方法,同步锁对象为:this // Tickets02 t = new Tickets02(); // Thread t0 = new Thread(t); // Thread t1 = new Thread(t); // Thread t2 = new Thread(t); // t0.start();t1.start();t2.start(); //第一次优化代码:同步静态方法,同步锁对象为:类名.class Tickets03 t = new Tickets03(); Thread t0 = new Thread(t); Thread t1 = new Thread(t); Thread t2 = new Thread(t); t0.start();t1.start();t2.start(); } }多线线程出现的问题package learn.javase.threads; /** 〇、原始多线程,会出现安全问题,会出现票卖完了还在卖,也就是会出现票为负数的情况 * @author Jole * */ public class Tickets00 implements Runnable{ //票的数量 private int ticket = 100; public void run() { while(true) { if(ticket>0) { //为了展示出现线程安全问题,此处让线程修改20毫秒 try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } //每次票减1 System.out.println(Thread.currentThread().getName()+ " " + ticket--); } } } }2、线程安全解决方案解决多线程问题方案使用同步锁,1、使用synchronized 2、使用Lock接口同步锁实现原理图:1、synchronized使用synchronized解决线程安全问题,使用公式: synchronized解决线程安全问题,使用公式: synchronized(锁(任意对象)){ 代码块 }1.1、synchronized加在外面:同步锁对象为objpackage learn.javase.threads; /** 一、原始线程同步解决,同步锁为obj * * synchronized解决线程安全问题,使用公式: * synchronized(锁(任意对象)){ * 代码块 * } * @author Jole * */ public class Tickets01 implements Runnable{ //票的数量 private int ticket = 100; Object obj = new Object(); public void run() { while(true) { //为了解决安全问题加入了synchronized关键字 synchronized (obj) { if(ticket>0) { //为了展示出现线程安全问题,此处让线程修改20毫秒 try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } //每次票减1 System.out.println(Thread.currentThread().getName()+ " " + ticket--); } } } } }1.2、synchronized加在非静态方法上:此时同步锁对象为thispackage learn.javase.threads; /** * 二、优化原始的线程安全解决办法,同步锁方法,同步锁对象为this * @author Jole * */ public class Tickets02 implements Runnable{ //总票数 private int tickets =100; //原始同步锁 // Object obj = new Object(); public void run() { bay(); } //第一次优化,将锁和代码块抽为一个方法,测试同步锁为obj // public void bay() { // while(true) { // synchronized (obj) { // if(tickets > 0) { // try { // Thread.sleep(20); // }catch(Exception e) { // e.printStackTrace(); // } // System.out.println(Thread.currentThread().getName()+" " +tickets-- ); // } // } // } // } //第二次进一步优化为,同步方法,此时同步锁为:this //测试就可以省去以前的obj对象的创建了,代码以前更占用少一点内存了 public synchronized void bay() { while(true) { if(tickets > 0) { try { Thread.sleep(20); }catch(Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" " + tickets--); } } } }1.3、synchronized加在静态方法上:此时同步锁对象为:类名.classpackage learn.javase.threads; /** * 静态同步方法,此时同步锁为:类名.class * 如此类中的同步锁对象是:Tickets03.class * @author Jole * */ public class Tickets03 implements Runnable{ //总票数 private static int tickets =100; public void run() { buy(); } //静态方法同步锁,此时代码的锁为:Tickets03.class public static synchronized void buy() { while(true) { if(tickets > 0) { try { Thread.sleep(20); }catch(Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" " + tickets--); } } } }2、Lock使用synchronized时,获取锁,释放锁都是JVM自动完成的,而通过Lock可以手动获取锁,释放锁。package learn.javase.threads; /** * 手动获取锁,释放锁 * @author Jole * */ public class ThreadShouDong { public static void main(String[] args) { ThreadRunnableDemo01 t = new ThreadRunnableDemo01(); Thread t0 = new Thread(t); Thread t1 = new Thread(t); Thread t2 = new Thread(t); t0.start(); t1.start(); t2.start(); } }Lock接口实现类:package learn.javase.threads; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 手动获取锁,释放锁 * @author Jole * */ public class ThreadRunnableDemo01 implements Runnable{ //票总数 private int tickets = 100; //锁 Lock接口的实现类ReentrantLock private Lock lock = new ReentrantLock(); public void run() { while(true) { //获取锁 lock.lock(); try { if(tickets > 0) { Thread.sleep(20); System.out.println(Thread.currentThread().getName()+" "+tickets--); } }catch(Exception e) { e.printStackTrace(); }finally { //释放锁 lock.unlock(); } } } }3、注意点使用多线程为了保证数据的安全性,加了synchronized或Lock保持同步,因此效率上就降低了。可能出现安全问题的情况:多线程,共享一个数据。使用synchronized时,同步锁必须保证为同一个同步锁对象才行。四、死锁死锁:多个线程,对方互相等待获取对方的锁。双方一直处于等待获取对方锁对象状态,也就是死锁。package learn.javase.threads; /** * 死锁:多个线程,对方互相等待获取对方的锁。双方一直处于等待获取对方锁对象状态,也就是死锁。 * @author Jole * */ public class DealThreadMainDemo { public static void main(String[] args) { // TODO Auto-generated method stub DetalThread death = new DetalThread(); Thread t0 = new Thread(death); Thread t1 = new Thread(death); t0.start(); t1.start(); } }同步锁对象A:package learn.javase.threads; /** * 同步锁A,对象 * @author Jole * */ public class LockA { private LockA() { }; public static final LockA lockA = new LockA(); }同步锁对象B:package learn.javase.threads; /** * 同步锁B,对象 * @author Jole * */ public class LockB { public LockB() { } public static final LockB lockB = new LockB(); }模拟出现死锁情况,实现类:package learn.javase.threads; public class DetalThread implements Runnable{ private int number = 0; public void run() { while(true) { if(number % 2 ==0) { synchronized (LockA.lockA) { System.out.println(Thread.currentThread().getName()+"线程,"+"第"+number+"次,"+"if抢到资源--获取到--同步锁A对象"); synchronized (LockB.lockB) { System.out.println(Thread.currentThread().getName()+"线程,"+"第"+number+"次,"+"if抢到资源--获取到--同步锁B对象"); } } }else { synchronized (LockB.lockB) { System.out.println(Thread.currentThread().getName()+"线程,"+"第"+number+"次,"+"else抢到资源--获取到--同步锁B对象"); synchronized (LockA.lockA) { System.out.println(Thread.currentThread().getName()+"线程,"+"第"+number+"次,"+"else抢到资源--获取到--同步锁A对象"); } } } number++; } } }注意获取的锁是那个。五、线程等待、唤醒实例:input拷贝数据对象,output输入对象:如果不是用线程等待与唤醒,出现问题:拷贝的数据太快,还来不及输出,可能出现输出的数据错乱。使用线程等待和唤醒,可以控制拷贝一个后,拷贝线程等待,然后输出线程输出,输出后,输出线程等待,然后拷贝线程唤醒,进行拷贝数据,依次循环。从而保证拷贝一个数据,输出一个数据。package learn.javase.threads; /** * 线程等待、唤醒: * 涉及同步锁是否是同一个锁,只有锁调用wait和notify才有效,不然抛出异常。 * 正常情况是交替出现,不会出现数据错乱情况。 * @author Jole * 本类是主测试类 * 该实例:input拷贝数据对象,output输入对象: * input可能出现拷贝对象拷贝很快,output输出对象很慢,导致output输出数据错乱,因此只能单个拷贝输出,且每次拷贝完后需wait等待,然后等输出完成后在拷贝。 * 因此需要拷贝完成后,等待,并唤醒输出,等输出完成前唤醒拷贝,然后自己在等待,然后拷贝又拷贝,然后又唤醒输出,如此循环。 * 实现方法加一个flag标记,是该拷贝还是输出 */ public class WatiNotifyThreadMainDemo { public static void main(String[] args) { //操作的是同一个对象锁,不然拷贝和输出都是用自己的锁this,也会发生数据错乱 ResourceData resource = new ResourceData(); //保证是同一个锁对象,所以都写了构造方法传入同一个锁对象 InputDemo input = new InputDemo(resource); OutputDemo output = new OutputDemo(resource); Thread t0 = new Thread(input); Thread t1 = new Thread(output); t0.start(); t1.start(); } }要拷贝的数据类:package learn.javase.threads; /** * 操作数据 * @author Jole * */ public class ResourceData { public String name; public String sex; public boolean flag; }拷贝线程:package learn.javase.threads; /** * 拷贝线程: * 当标记flag=true时,拷贝完成,需等待 * @author Jole * */ public class InputDemo implements Runnable { private ResourceData resourceData; public InputDemo(ResourceData resourceData) { this.resourceData = resourceData; } @Override public void run() { int number=0; while(true) { synchronized (resourceData) { //如果为ture拷贝完成,需等待 if(resourceData.flag) { try { resourceData.wait(); }catch(Exception e) { e.printStackTrace(); } } //拷贝值,比如写死拷贝张三,30岁,lishi,20岁 if(number % 2==0) { resourceData.name = "张三"; resourceData.sex = "男"; }else { resourceData.name = "Lishi"; resourceData.sex = "nv"; } //设置true,以便下次进入等待wait resourceData.flag = true; //唤醒输出线程 resourceData.notify(); } number++; } } }输出线程:package learn.javase.threads; /** * 输出线程: * 当标记flag=false时,输出完成,需等待 * @author Jole * */ public class OutputDemo implements Runnable { private ResourceData resourceData; public OutputDemo(ResourceData resourceData) { this.resourceData = resourceData; } @Override public void run() { while(true) { synchronized (resourceData) { //如果为false,则输出完成,等待 if(!resourceData.flag) { try { resourceData.wait(); }catch(Exception e) { e.printStackTrace(); } } //否则输出,并唤醒拷贝 System.out.println(resourceData.name+" .. "+ resourceData.sex); resourceData.flag = false; resourceData.notify(); } } } }
2022年02月28日
17 阅读
0 评论
1 点赞
1
...
3
4
5
6